Dlaczego „&&”, a nie „&”?

135

Dlaczego jest &&lepsze &i ||lepsze niż |?

Zapytałem kogoś, kto programuje od lat, a jego wyjaśnienie brzmiało:

Na przykład, w if (bool1 && bool2 && bool3) { /*DoSomething*/ }, bool1musi być prawdziwe, aby test bool2był prawdziwy, co musi być prawdziwe przed przejściem do bool3, itp. Gdybym &zamiast tego użył pojedynczego, nie ma kolejności testu, nawet jeśli wszystkie muszą być zgodne z przejść do następnej linii, więc dlaczego to ma znaczenie?

Uwaga: chciałbym zwrócić uwagę, że jestem programistycznym odpowiednikiem małego dziecka i nie jest to poważne ani pilne pytanie. Bardziej chodzi o zrozumienie, dlaczego rzeczy należy robić w określony sposób, a nie w inny.

Dani
źródło
55
& i && | i || to zupełnie inni operatorzy
Felice Pollano
3
Zmieniono tag, ponieważ dotyczy to nie tylko języka C #
Jonathan Dickinson,
12
Cofnięto, ponieważ odpowiedzi są już specyficzne dla języka C #, a wewnętrzne działanie może się nieco różnić w innych językach, które mają ogólnie tę samą koncepcję.
Daniel Hilgarth,
8
@Felice: Są różne, ale prawie nie różnią się od siebie. W rzeczywistości są one bardzo podobne: x & yi x && yzawsze będą zwracać ten sam wynik, jeśli x i y są wyrażeniami typu boolowskiego. W rzeczywistości jedyną różnicą w tym przypadku wydaje się być to x & y, że in , y jest zawsze oceniane.
Joren,
1
@slawekin: Proponuję przeczytać odpowiedzi. Niektórzy obszernie piszą o różnicach w wydajności. Odpowiedź może cię jednak zaskoczyć.
Abel

Odpowiedzi:

183

W większości przypadków &&i ||są preferowane w stosunku &do |tych pierwszych, ponieważ są one zwarte, co oznacza, że ​​ocena jest anulowana, gdy tylko wynik jest jasny.

Przykład:

if(CanExecute() && CanSave())
{
}

Jeśli CanExecutezwraca false, całe wyrażenie będzie false, niezależnie od zwracanej wartości CanSave. Z tego powodu CanSavenie jest wykonywany.

Jest to bardzo przydatne w następujących okolicznościach:

string value;
if(dict.TryGetValue(key, out value) && value.Contains("test"))
{
    // Do Something
}

TryGetValuezwraca, falsejeśli podany klucz nie zostanie znaleziony w słowniku. Ze względu na zwarciowy charakter programu &&, value.Contains("test")jest wykonywany tylko wtedy, gdy TryGetValuepowraca, truea zatem valuenie jest null. Jeśli zamiast tego użyjesz operatora bitowego AND& , otrzymasz a, NullReferenceExceptionjeśli klucz nie zostanie znaleziony w słowniku, ponieważ w każdym przypadku druga część wyrażenia jest wykonywana.

Podobnym, ale prostszym przykładem tego jest następujący kod (jak wspomniał TJHeuvel):

if(op != null && op.CanExecute())
{
    // Do Something
}

CanExecutejest wykonywany tylko wtedy, gdy opnie jest null. Jeśli optak null, pierwsza część wyrażenia ( op != null) jest obliczana do, falsea ocena reszty ( op.CanExecute()) jest pomijana.

Niezależnie od tego, technicznie są one różne, za:
&&i ||mogą być używane tylko na boolzaś &i |może być używany na dowolnej integralną typu ( bool, int, long, sbyte, ...), ponieważ są operatory bitowe. &jest bitowym operatorem AND i |jest bitowym operatorem OR .

Aby być bardzo dokładnym, w C # te operatory ( &, |[i ^]) nazywane są „operatorami logicznymi” (zobacz specyfikację C # , rozdział 7.11). Istnieje kilka implementacji tych operatorów:

  1. Dla liczb całkowitych ( int, uint, longi ulong, rozdział 7.11.1):
    są one realizowane, aby obliczyć wynik bitowe argumentów i operatora, to znaczy &jest wdrożenie do obliczenia bitowe logiczne ANDitd
  2. W przypadku wyliczeń (rozdział 7.11.2):
    są one zaimplementowane w celu wykonania operacji logicznej podstawowego typu wyliczenia.
  3. Dla wartości logicznych i wartości logicznych dopuszczających wartość zerową (rozdziały 7.11.3 i 7.11.4):
    Wynik nie jest obliczany przy użyciu obliczeń bitowych. Wynik jest zasadniczo wyszukiwany na podstawie wartości dwóch operandów, ponieważ liczba możliwości jest tak mała.
    Ponieważ obie wartości są używane do wyszukiwania, ta implementacja nie powoduje zwarcia.
Daniel Hilgarth
źródło
31
Może to być również przydatne do sprawdzania, czy coś jest zerowe. Na przykład: if(op != null && op.CanExecute()). Ponieważ druga przyczyna nie jest oceniana, gdy pierwsza nie jest prawdziwa, jest to poprawne.
TJHeuvel,
2
@TJHeuvel: Jest to w zasadzie to samo użycie, które opisałem na moim TryGetValueprzykładzie. Ale tak, to kolejny dobry przykład.
Daniel Hilgarth,
4
Dobra odpowiedź. Może powinieneś również dodać przykład, jak &lub |jest używany z argumentami innymi niż bool (tj. Co robią operatorzy) z korzyścią dla wszystkich nowych osób.
Zabba,
81

Aby bardzo jasno wyjaśnić, co to oznacza (nawet jeśli inne odpowiedzi na to wskazują - ale prawdopodobnie użyj terminologii, której nie rozumiesz).

Poniższy kod:

if (a && b)
{
   Foo();
}

Jest naprawdę skompilowany do tego:

if (a)
{
    if (b)
    {
        Foo();
    }
}

Gdzie następujący kod jest kompilowany dokładnie tak, jak jest reprezentowany:

if (a & b)
{
   Foo();
}

Nazywa się to zwarciem. Ogólnie rzecz biorąc, zawsze powinieneś używać &&iw ||swoich warunkach.

Znaki bonusowe: jest jeden scenariusz, w którym nie powinieneś. Jeśli jesteś w sytuacji, w której wydajność ma kluczowe znaczenie (a to jest kluczowe w nanosekundach ), używaj zwarcia tylko wtedy, gdy musisz (np. nullSprawdzanie) - ponieważ zwarcie jest rozgałęzieniem / skokiem; co może spowodować błędną prognozę gałęzi na twoim CPU; &jest znacznie tańsze niż &&. Jest też scenariusz, w którym zwarcie może faktycznie złamać logikę - spójrz na moją odpowiedź .

Diatryba / Monolog : Odnośnie błędnych przewidywań dotyczących gałęzi, które najbardziej błogo ignorują. Cytując Andy'ego Firtha (który pracował nad grami od 13 lat): „To może być niższy poziom, niż ludzie myślą, że powinni przejść ... ale byliby w błędzie. wpływają na wydajność w OGROMNYM stopniu ... o wiele bardziej niż większość programistów może docenić re: śmierć przez tysiąc cięć ”.

  • Twórcy gier (i inni pracujący w ekstremalnych warunkach czasu rzeczywistego) posuwają się nawet do restrukturyzacji swojej logiki, aby lepiej dopasować ją do predyktora. Istnieją również dowody na to w zdekompilowanym kodzie mscorlib.
  • To, że .NET chroni Cię przed tego typu rzeczami, nie oznacza, że ​​nie jest to ważne. Błędne przewidywanie gałęzi jest strasznie kosztowne przy 60 Hz; lub przy 10.000 żądań na sekundę.
  • Intel nie miałby narzędzi do identyfikacji lokalizacji błędnych przewidywań, ani Windows nie miałby do tego licznika wydajności, ani też nie byłoby słowa, które by to opisało, gdyby nie był to problem.
  • Nieznajomość niższych poziomów i architektury nie sprawia, że ​​ktoś, kto jest ich świadomy, myli się.
  • Zawsze staraj się zrozumieć ograniczenia sprzętu, nad którym pracujesz.

Oto punkt odniesienia dla niewierzących. Najlepiej uruchomić proces w czasie rzeczywistym / wysokim, aby złagodzić wpływ harmonogramu: https://gist.github.com/1200737

Jonathan Dickinson
źródło
7
O „znakach bonusowych”: Wszyscy wiemy, jakie korzyści przynosi przedwczesna optymalizacja. :)
CVn
6
@Michael - dlatego „nano-sekund kluczowe” jest pogrubione :). Twórcy gier AAA zazwyczaj martwią się takimi rzeczami - i nigdy nie wiadomo, kto przeczyta odpowiedzi; więc zawsze najlepiej jest dokumentować nawet przypadki graniczne / skrajne.
Jonathan Dickinson,
1
Czy ten znak bonusowy jest ważny dla C #? Myślałem, że nie, ponieważ MSIL jest interpretowany, chyba że wyrażenie jest skompilowane aż do kodu maszynowego.
Jeremy McGee,
7
@Jeremy MSIL nie jest interpretowane.
Jonathan Dickinson,
2
@TheD ponownie sprawdź odpowiedź - dodałem monolog o tym, dlaczego POWINIENEŚ się tym martwić. FYI, (x && y)przekłada się na LOAD x; BRANCH_FALSE; LOAD y; BRANCH_FALSE;gdzie (x & y)przekłada się na LOAD x; LOAD y; AND; BRANCH_FALSE;. Jedna gałąź kontra dwie.
Jonathan Dickinson,
68

Operator logiczny ( ||and &&) a operator bitowy ( |and &).

Najważniejsza różnica między operatorem logicznym a operatorem bitowym polega na tym, że operator logiczny przyjmuje dwie wartości logiczne i tworzy wartość logiczną, podczas gdy operator bitowy przyjmuje dwie liczby całkowite i tworzy liczbę całkowitą (uwaga: liczby całkowite oznaczają dowolny typ danych całkowitych, a nie tylko int).

Aby być pedantycznym, operator bitowy przyjmuje wzorzec bitowy (np. 01101011) i wykonuje bitowe AND / OR na każdym bicie. Na przykład, jeśli masz dwie 8-bitowe liczby całkowite:

a     = 00110010 (in decimal:    32+16+2   = 50)
b     = 01010011 (in decimal: 64+   16+2+1 = 83)
----------------
a & b = 00010010 (in decimal:       16+2   = 18)
a | b = 01110011 (in decimal: 64+32+16+2+1 = 115)

podczas gdy operator logiczny działa tylko w bool:

a      = true
b      = false
--------------
a && b = false
a || b = true

Po drugie, często jest możliwe użycie operatora bitowego na bool, ponieważ prawda i fałsz są równoważne odpowiednio 1 i 0, i zdarza się, że jeśli przetłumaczysz true na 1 i false na 0, wykonaj operację bitową, a następnie przekonwertuj wartość niezerową na prawdę i zero na fałsz; zdarza się, że wynik będzie taki sam, gdybyś użył właśnie operatora logicznego (sprawdź to w ćwiczeniu).

Inną ważną różnicą jest również to, że operator logiczny jest zwarty . Dlatego w niektórych kręgach [1] często można zobaczyć, jak ludzie robią coś takiego:

if (person && person.punch()) {
    person.doVictoryDance()
}

co oznacza: „jeśli osoba istnieje (tj. nie jest zerowa), spróbuj uderzyć ją / ją, a jeśli cios się powiedzie (tj. zwróci prawdę), wykonaj taniec zwycięstwa” .

Gdybyś zamiast tego użył operatora bitowego, to:

if (person & person.punch()) {
    person.doVictoryDance()
}

przetłumaczy się na: "jeśli osoba istnieje (tj. nie jest zerowa) i cios się powiedzie (tj. zwróci prawdę), wtedy wykonaj taniec zwycięstwa" .

Zwróć uwagę, że w operatorze logicznym zwartym person.punch()kod może w ogóle nie zostać uruchomiony, jeśli personma wartość null. W rzeczywistości w tym konkretnym przypadku drugi kod spowodowałby błąd odwołania zerowego, gdyby personbył zerowy, ponieważ próbuje wywołać person.punch()bez względu na to, czy osoba jest pusta, czy nie. To zachowanie polegające na nieocenianiu prawidłowego operandu nazywane jest zwarciem .

[1] Niektórzy programiści wzbraniają się umieszczać wywołanie funkcji, które ma efekt uboczny wewnątrz ifwyrażenia, podczas gdy dla innych jest to powszechny i ​​bardzo przydatny idiom.

Ponieważ operator bitowy działa jednocześnie na 32-bitowych bitach (jeśli korzystasz z maszyny 32-bitowej), może to prowadzić do bardziej eleganckiego i szybszego kodu, jeśli chcesz porównać ogromną liczbę warunków, np.

int CAN_PUNCH = 1 << 0, CAN_KICK = 1 << 1, CAN_DRINK = 1 << 2, CAN_SIT = 1 << 3,
    CAN_SHOOT_GUNS = 1 << 4, CAN_TALK = 1 << 5, CAN_SHOOT_CANNONS = 1 << 6;

Person person;
person.abilities = CAN_PUNCH | CAN_KICK | CAN_DRINK | CAN_SIT | CAN_SHOOT_GUNS;

Place bar;
bar.rules = CAN_DRINK | CAN_SIT | CAN_TALK;

Place military;
military.rules = CAN_SHOOT_CANNONS | CAN_PUNCH | CAN_KICK | CAN_SHOOT_GUNS | CAN_SIT;

CurrentLocation cloc1, cloc2;
cloc1.usable_abilities = person_abilities & bar_rules;
cloc2.usable_abilities = person_abilities & military_rules;

// cloc1.usable_abilities will contain the bit pattern that matches `CAN_DRINK | CAN_SIT`
// while cloc2.usable_abilities will contain the bit pattern that matches `CAN_PUNCH | CAN_KICK | CAN_SHOOT_GUNS | CAN_SIT`

Zrobienie tego samego z operatorami logicznymi wymagałoby niezręcznej ilości porównań:

Person person;
person.can_punch = person.can_kick = person.can_drink = person.can_sit = person.can_shoot_guns = true;
person.can_shoot_cannons = false;

Place bar;
bar.rules.can_drink = bar.rules.can_sit = bar.rules.can_talk = true;
bar.rules.can_punch = bar.rules.can_kick = bar.rules.can_shoot_guns = bar.rules.can_shoot_cannons = false;

Place military;
military.rules.can_punch = military.rules.can_kick = military.rules.can_shoot_guns = military.rules.can_shoot_cannons = military.rules.can_sit = true;
military.rules.can_drink = military.rules.can_talk = false;

CurrentLocation cloc1;
bool cloc1.usable_abilities.can_punch         = bar.rules.can_punch         && person.can_punch,
     cloc1.usable_abilities.can_kick          = bar.rules.can_kick          && person.can_kick,
     cloc1.usable_abilities.can_drink         = bar.rules.can_drink         && person.can_drink,
     cloc1.usable_abilities.can_sit           = bar.rules.can_sit           && person.can_sit,
     cloc1.usable_abilities.can_shoot_guns    = bar.rules.can_shoot_guns    && person.can_shoot_guns,
     cloc1.usable_abilities.can_shoot_cannons = bar.rules.can_shoot_cannons && person.can_shoot_cannons
     cloc1.usable_abilities.can_talk          = bar.rules.can_talk          && person.can_talk;

bool cloc2.usable_abilities.can_punch         = military.rules.can_punch         && person.can_punch,
     cloc2.usable_abilities.can_kick          = military.rules.can_kick          && person.can_kick,
     cloc2.usable_abilities.can_drink         = military.rules.can_drink         && person.can_drink,
     cloc2.usable_abilities.can_sit           = military.rules.can_sit           && person.can_sit,
     cloc2.usable_abilities.can_shoot_guns    = military.rules.can_shoot_guns    && person.can_shoot_guns,
     cloc2.usable_abilities.can_talk          = military.rules.can_talk          && person.can_talk,
     cloc2.usable_abilities.can_shoot_cannons = military.rules.can_shoot_cannons && person.can_shoot_cannons;

Klasycznym przykładem użycia wzorców bitowych i operatorów bitowych są uprawnienia systemu plików Unix / Linux.

Lie Ryan
źródło
3
+1 za stronę wpływa na problem. Zaskoczony, że nie wspomniano wcześniej
Conrad Frix,
3
przykład wydaje się nieco brutalny, ale wydaje się, że inne odpowiedzi zbytnio skupiały się na zwarciu, a za mało na różnicy między operacjami na liczbach całkowitych i na liczbach logicznych.
R0MANARMY
Funkcję należy zrozumieć, zanim pojawią się szczegóły implementacji (krótki cykl / skutki uboczne). Cieszę się, że wyjaśniłeś główną różnicę, która polega na logice logicznej i całkowitej, a nie na zwarciu.
Abel
@ROMANARMY - brutalny, uwielbiam ironię, jaką daje twój mnich.
Dobra
8

W przypadku:

if (obj != null && obj.Property == true) { }

działałby zgodnie z oczekiwaniami.

Ale:

if (obj != null & obj.Property == true) { }

może potencjalnie zgłosić wyjątek odwołania o wartości null.

tłuszczowy
źródło
2

Krótko i prosto:

1 && 2= prawda,
ponieważ
1 = prawda (niezerowa) w C
2 = prawda (niezerowa) w C

trueANDS logicznie truedaje true.

Ale

1 & 2= 0 = fałsz,
ponieważ
1 = 0001 binarnie
2 = 0010 binarnie

0001 AND s bitowe z 0010, aby dać 0000 = 0 dziesiętnie.

Podobnie dla || i | operatorzy też ...!

Shrey
źródło
2
-1: Mówimy tutaj o C # ... 1 && 2jest nielegalne w C #
Daniel Hilgarth,
Ale to jest niezwykle ważny przykład, który wyjaśnia, dlaczego nie można po prostu zamienić znaków & i && (co wielu osobom wydaje się myśleć).
bobobobo
1

&&jest wersją zwarciową &.

Jeśli oceniamy false & true , wiemy już patrząc na pierwszy argument, że wynik będzie fałszywy. &&Wersja operator zwróci wynik tak szybko jak to możliwe, zamiast oceniać cały wyraz. Jest też podobny Verion od |operatora ||.

mdm
źródło
1
if (list.Count() > 14 && list[14] == "foo")

jest bezpieczny

if (list.Count() > 14 & list[14] == "foo")

ulegnie awarii, jeśli lista nie będzie miała odpowiedniego rozmiaru.

jalf
źródło
Nie wyobrażam sobie, że ktoś może napisać „if (list.Count ()> 14 & list [14] ==„ foo ”)” zamiast „if (list.Count ()> 14 && list [14] ==” bla")". & po prostu po prostu i naturalnie nie może być użyte dla && w tym przypadku, nawet jeśli & jest na pewno bezpieczne (na przykład lista [1]).
Tien Do
1

Operatory C # powinni wyjaśnić, dlaczego:

Zasadniczo posiadanie dwóch &lub |oznacza, że ​​jest to warunkowe, a nie logiczne, więc możesz odróżnić te dwa.

& Operator ma przykład użycia jednego&.

Stuart Thomson
źródło
Oba łącza są (skutecznie) uszkodzone (przekierowania do „Dokumentacji programu Visual Studio 2005 Retired” ).
Peter Mortensen
1

OK, według wartości nominalnej

    Boolean a = true;
    Boolean b = false;

    Console.WriteLine("a({0}) && b({1}) =  {2}", a, b, a && b);
    Console.WriteLine("a({0}) || b({1}) =  {2}", a, b, a || b);
    Console.WriteLine("a({0}) == b({1}) =  {2}", a, b, a == b);

    Console.WriteLine("a({0}) & b({1}) =  {2}", a, b, a & b);
    Console.WriteLine("a({0}) | b({1}) =  {2}", a, b, a | b);
    Console.WriteLine("a({0}) = b({1}) =  {2}", a, b, a = b);

dają tę samą odpowiedź. Jednak, jak pokazałeś, jeśli masz bardziej złożone pytanie, to:

if (a and b and c and d) ..

Jeśli anie jest prawdą i może bjest to funkcja, w której musi się wyłączyć, połączyć się z czymś, uzyskać to, zrobić to, podjąć decyzję ... po co się przejmować? Strata czasu, ty wiesz, że to już nie powiodło się. Po co wyłączać maszynę i wykonywać dodatkową bezcelową pracę?

Zawsze używałem, &&ponieważ najpierw stawiam najbardziej prawdopodobne niepowodzenie, a więc mniej obliczeń, zanim przejdę dalej, gdy nie ma sensu. Jeśli nie ma sposobu, aby przewidzieć mniej prawdopodobne wybory, na przykład jeśli masz wartość logiczną, aby ograniczyć wytwarzanie danych, na przykład:

if (limit && !MyDictionary.ContainsKey("name")) 
    continue;

Jeśli tak nie jest limit, nie przejmuj się sprawdzaniem klucza, co może potrwać dłużej.

BugFinder
źródło
1

W przypadku użycia w wyrażeniu logicznym, takim jak instrukcja if, &&preferowane, ponieważ przestanie oceniać wyrażenia, gdy tylko napotkany zostanie pierwszy fałszywy wynik. Jest to możliwe, ponieważ fałszywa wartość spowoduje, że całe wyrażenie będzie fałszywe. Podobnie (i znowu w wyrażeniach logicznych)|| jest preferowane, ponieważ przestanie oceniać wyrażenia, gdy tylko napotka prawdziwe wyrażenie, ponieważ każda prawdziwa wartość spowoduje, że całe wyrażenie będzie prawdziwe.

Jeśli jednak wyrażenia, które są redagowane lub redagowane razem, mają skutki uboczne i chcesz, aby wszystko to wydarzyło się w wyniku twojego wyrażenia (niezależnie od wyniku wyrażenia logicznego), wtedy &i |może być użyte. Z drugiej strony operatory &&i ||mogą być przydatne jako zabezpieczenia przed niepożądanymi efektami ubocznymi (takimi jak wskaźnik zerowy powodujący zgłoszenie wyjątku).

&I |operatorzy mogą być również stosowane z liczb i w tym przypadku to wywołać efekt liczbą całkowitą, która ma dwa operandy ED lub OR, ed razem na poziomie bitów. Może to być przydatne, gdy bity binarne wartości całkowitej są używane jako tablica wartości true i false. Aby sprawdzić, czy określony bit jest włączony lub wyłączony, maska ​​bitowa jest bitowa i edytowana z wartością. Aby nieco włączyć tę samą maskę, można ją bitowo lub edytować z wartością. Wreszcie, aby nieco wyłączyć, dopełnienie bitowe (użycie ~) maski jest bitowe i -ed z wartością.

int a = 0; // 0 means all bits off
a = a | 4; // set a to binary 100
if ((a & 4) != 0) {
    // will do something
}
a = a & (~4) // turn bit off again, a is now 000

W językach innych niż C # należy zachować ostrożność w przypadku trybów logicznych i bitowych w & i |. W powyższym kodzie ifwyrażenie warunkowe w instrukcji (a & 4) != 0jest bezpiecznym sposobem wyrażenia tego warunku, ale w wielu językach podobnych do języka C instrukcje warunkowe mogą po prostu traktować zerowe wartości całkowite jako fałszywe, a niezerowe wartości całkowite jako prawdziwe. (Przyczyna tego dotyczy dostępnych warunkowych instrukcji procesora rozgałęzienia i ich związku ze znacznikiem zera, który jest aktualizowany po każdej operacji na liczbach całkowitych). Zatem ìftest instrukcji na zero można usunąć, a warunek skrócić do (a & 4).

Może to powodować zamieszanie, a może nawet problemy, gdy wyrażenia połączone przy użyciu bitowych i operatorów zwracają wartości, które nie mają wyrównanych bitów. Rozważ następujący przykład, w którym efekty uboczne dwóch funkcji są pożądane, przed sprawdzeniem, czy obie zakończyły się pomyślnie (zgodnie z ich definicją, zwracając wartość niezerową):

if (foo() & bar()) {
    // do something
}

W C, jeśli foo()zwraca 1 i bar()zwraca 2, „coś” nie zostanie wykonane, ponieważ 1 & 2wynosi zero.

C # wymaga instrukcji warunkowych, takich jak ifposiadanie wartości logicznej oeprand, a język nie pozwala na rzutowanie wartości całkowitej na wartość logiczną. Więc powyższy kod generowałby błędy kompilatora. Byłoby to bardziej poprawnie wyrażone w następujący sposób:

if (foo() != 0 & bar() != 0) {
    // do something
}
graza
źródło
1

Jeśli jesteś starym programistą C, bądź ostrożny . C # naprawdę mnie zaskoczył.

MSDN mówi dla |operatora:

Binarny | operatory są wstępnie zdefiniowane dla typów całkowitych i bool . Dla typów całkowitych | oblicza bitowe OR swoich operandów. Dla operandów bool | oblicza logiczne LUB swoich operandów; to znaczy, wynik jest fałszywy wtedy i tylko wtedy, gdy oba jego operandy są fałszywe.

(Podkreślenie jest moje). Typy boolowskie są traktowane specjalnie iw tym kontekście pytanie tylko zaczyna mieć sens, a różnica jest taka, jak inne już wyjaśnione w ich odpowiedziach:

&&i ||powodują zwarcie. &i |oblicz oba operandy.

a to, co jest lepsze, zależy od wielu rzeczy, takich jak skutki uboczne, wydajność i czytelność kodu, ale generalnie operatory zwarciowe są preferowane także dlatego, że są lepiej rozumiane przez osoby z podobnym doświadczeniem, jak ja.

Powód jest taki: chciałbym argumentować w ten sposób: ponieważ w C nie ma prawdziwego typu boolowskiego, można użyć operatora bitowego |i ocenić jego wynik jako prawdziwy lub fałszywy w warunku if. Ale to jest złe podejście do C #, ponieważ istnieje już specjalny przypadek dla typów logicznych.

nalply
źródło
0

Jest to ważne, ponieważ jeśli koszt oceny bool2 (na przykład) jest wysoki, ale bool1 jest fałszywe, zaoszczędziłeś sobie sporo obliczeń, używając && over &

wydający
źródło
0

Ponieważ &&i ||są używane do kontroli przepływu, tak jak if/elsesą. Nie zawsze chodzi o warunki warunkowe. Całkowicie rozsądne jest napisanie jako oświadczenia, a nie warunku iflub whilewarunku, co następuje:

 a() && b() && c() && d();

lub nawet

 w() || x() || y() || z();

Nie chodzi tylko o to, że są one łatwiejsze do wpisania niż ich odpowiedniki if/else; są również dużo łatwiejsze do odczytania i zrozumienia.

tchrist
źródło
0

&& i & oznaczają dwie bardzo różne rzeczy i dają dwie różne odpowiedzi.

1 && 2daje 1 („prawda”)
1 & 2daje 0 („fałsz”)

&&jest operatorem logicznym - oznacza to, że „prawda, jeśli oba operandy są prawdziwe”
&jest porównaniem bitowym. To znaczy „powiedz mi, który z bitów jest ustawiony w obu operandach”

tylerl
źródło
2
Pytanie dotyczy C #. W C # nie ma sposobu na rzutowanie liczby na bool, więc 0 nie jest „fałszem”, a wartość niezerowa nie jest „prawdą”; po prostu nie ma równoważności.
Nate CK
Aby przekonwertować liczbę na bool, w taki sposób, że 1 oznacza prawdę, a 0 oznacza fałsz, powiedz „n! = 0” (zakładam, że C # nie jest mi obeznany). Właściwie chciałem wycofać ten komentarz, ponieważ nie jest dobrze zbadany i nie sądzę, aby był pomocny lub naprawdę istotny w stosunku do poprzedniego komentarza teraz, kiedy myślę o nim więcej, ale przypadkowo wcisnąłem Enter i teraz nie sądzę, żebym mógł anuluj to, więc gotowe, cokolwiek warto :-)
Don Hatch,
1 && 2wyświetla błąd kompilatora: „Błąd 4 Operator '&&' nie może być zastosowany do operandów typu 'int' i 'int'”
Peter Mortensen
0

Najszybszym (i nieco głupim) sposobem wyjaśnienia tego ludziom, którzy nie MUSZĄ znać dokładnych operacji kodu podczas robienia tego, jest

&& sprawdza każdy z tych warunków, znajdzie fałsz i zwróci cały wynik jako fałszywy

|| sprawdza każdy z tych warunków, znajdzie prawdę i zwróci cały wynik jako prawdziwy.

i robi oparte na matematyce aponowanie ZARÓWNO / WSZYSTKICH warunków i radzi sobie z wynikiem.

|robi oparte na matematyce aponowanie ZARÓWNO / WSZYSTKIE warunki i radzenie sobie z wynikiem.

Nigdy nie natknąłem się na punkt, w którym musiałem użyć & lub | w instrukcji if. Używam go głównie do cięcia wartości szesnastkowych na kolory składowe za pomocą przesunięcia bitowego.

NA PRZYKŁAD:

r = fullvalue >> 0xFF & 0xFF;
g = fullvalue >> 0xF & 0xFF;
b = fullvalue & 0xFF;

W ramach tej operacji „& 0xFF” wymusza patrzenie tylko na wartość binarną. Nie znalazłem osobiście zastosowania dla | jeszcze chociaż.

WORMSS
źródło
0

Po prostu,

if exp1 && exp2

jeśli exp1 to flase nie sprawdzaj exp2

ale

if exp1 & exp2

jeśli exp1 jest falseLub true sprawdź exp2

i rzadko ludzie używają, &ponieważ rzadko chcą sprawdzić exp2, czy exp1 jestfalse

Fady
źródło