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*/ }
, bool1
musi być prawdziwe, aby test bool2
był 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.
x & y
ix && y
zawsze 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ć tox & y
, że in , y jest zawsze oceniane.Odpowiedzi:
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:
Jeśli
CanExecute
zwracafalse
, całe wyrażenie będziefalse
, niezależnie od zwracanej wartościCanSave
. Z tego powoduCanSave
nie jest wykonywany.Jest to bardzo przydatne w następujących okolicznościach:
TryGetValue
zwraca,false
jeśli podany klucz nie zostanie znaleziony w słowniku. Ze względu na zwarciowy charakter programu&&
,value.Contains("test")
jest wykonywany tylko wtedy, gdyTryGetValue
powraca,true
a zatemvalue
nie jestnull
. Jeśli zamiast tego użyjesz operatora bitowego AND&
, otrzymasz a,NullReferenceException
jeś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):
CanExecute
jest wykonywany tylko wtedy, gdyop
nie jestnull
. Jeśliop
taknull
, pierwsza część wyrażenia (op != null
) jest obliczana do,false
a ocena reszty (op.CanExecute()
) jest pomijana.Niezależnie od tego, technicznie są one różne, za:
&&
i||
mogą być używane tylko nabool
zaś&
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:int
,uint
,long
iulong
, rozdział 7.11.1):są one realizowane, aby obliczyć wynik bitowe argumentów i operatora, to znaczy
&
jest wdrożenie do obliczenia bitowe logiczneAND
itdsą one zaimplementowane w celu wykonania operacji logicznej podstawowego typu wyliczenia.
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.
źródło
if(op != null && op.CanExecute())
. Ponieważ druga przyczyna nie jest oceniana, gdy pierwsza nie jest prawdziwa, jest to poprawne.TryGetValue
przykładzie. Ale tak, to kolejny dobry przykład.&
lub|
jest używany z argumentami innymi niż bool (tj. Co robią operatorzy) z korzyścią dla wszystkich nowych osób.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:
Jest naprawdę skompilowany do tego:
Gdzie następujący kod jest kompilowany dokładnie tak, jak jest reprezentowany:
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.
null
Sprawdzanie) - 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ęć ”.
Oto punkt odniesienia dla niewierzących. Najlepiej uruchomić proces w czasie rzeczywistym / wysokim, aby złagodzić wpływ harmonogramu: https://gist.github.com/1200737
źródło
(x && y)
przekłada się naLOAD x; BRANCH_FALSE; LOAD y; BRANCH_FALSE;
gdzie(x & y)
przekłada się naLOAD x; LOAD y; AND; BRANCH_FALSE;
. Jedna gałąź kontra dwie.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:
podczas gdy operator logiczny działa tylko w
bool
: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:
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:
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śliperson
ma wartość null. W rzeczywistości w tym konkretnym przypadku drugi kod spowodowałby błąd odwołania zerowego, gdybyperson
był 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
if
wyraż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.
Zrobienie tego samego z operatorami logicznymi wymagałoby niezręcznej ilości porównań:
Klasycznym przykładem użycia wzorców bitowych i operatorów bitowych są uprawnienia systemu plików Unix / Linux.
źródło
W przypadku:
działałby zgodnie z oczekiwaniami.
Ale:
może potencjalnie zgłosić wyjątek odwołania o wartości null.
źródło
Krótko i prosto:
1 && 2
= prawda,ponieważ
1 = prawda (niezerowa) w C
2 = prawda (niezerowa) w C
true
ANDS logicznietrue
dajetrue
.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ż ...!
źródło
1 && 2
jest nielegalne w C #&&
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||
.źródło
jest bezpieczny
ulegnie awarii, jeśli lista nie będzie miała odpowiedniego rozmiaru.
źródło
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
&
.źródło
OK, według wartości nominalnej
dają tę samą odpowiedź. Jednak, jak pokazałeś, jeśli masz bardziej złożone pytanie, to:
Jeśli
a
nie jest prawdą i możeb
jest 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:Jeśli tak nie jest
limit
, nie przejmuj się sprawdzaniem klucza, co może potrwać dłużej.źródło
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ą.W językach innych niż C # należy zachować ostrożność w przypadku trybów logicznych i bitowych w & i |. W powyższym kodzie
if
wyrażenie warunkowe w instrukcji(a & 4) != 0
jest 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ìf
test 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ą):
W C, jeśli
foo()
zwraca 1 ibar()
zwraca 2, „coś” nie zostanie wykonane, ponieważ1 & 2
wynosi zero.C # wymaga instrukcji warunkowych, takich jak
if
posiadanie 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:źródło
Jeśli jesteś starym programistą C, bądź ostrożny . C # naprawdę mnie zaskoczył.
MSDN mówi dla
|
operatora:(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:
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.źródło
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 &
źródło
Ponieważ
&&
i||
są używane do kontroli przepływu, tak jakif/else
są. Nie zawsze chodzi o warunki warunkowe. Całkowicie rozsądne jest napisanie jako oświadczenia, a nie warunkuif
lubwhile
warunku, co następuje:lub nawet
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.źródło
&& i & oznaczają dwie bardzo różne rzeczy i dają dwie różne odpowiedzi.
1 && 2
daje 1 („prawda”)1 & 2
daje 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”źródło
1 && 2
wyświetla błąd kompilatora: „Błąd 4 Operator '&&' nie może być zastosowany do operandów typu 'int' i 'int'”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, aż znajdzie fałsz i zwróci cały wynik jako fałszywy
|| sprawdza każdy z tych warunków, aż 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:
W ramach tej operacji „& 0xFF” wymusza patrzenie tylko na wartość binarną. Nie znalazłem osobiście zastosowania dla | jeszcze chociaż.
źródło
Po prostu,
if exp1 && exp2
jeśli exp1 to
flase
nie sprawdzaj exp2ale
if exp1 & exp2
jeśli exp1 jest
false
Lubtrue
sprawdź exp2i rzadko ludzie używają,
&
ponieważ rzadko chcą sprawdzić exp2, czy exp1 jestfalse
źródło