Pourquoi &&
Est préférable à &
Et ||
Préférable à |
?
J'ai demandé à quelqu'un qui programme depuis des années et son explication était:
Par exemple, dans if (bool1 && bool2 && bool3) { /*DoSomething*/ }
, bool1
Doit être vrai pour qu'il teste bool2
Qui doit être vrai avant de passer à bool3
, Etc. Si j'avais utilisé un seul &
À la place, il n'y a pas d'ordre pour le test même si tous doivent être vrais pour passer à la ligne suivante, alors pourquoi est-ce important de toute façon?
Remarque: je voudrais souligner que je suis l'équivalent en programmation d'un tout-petit et que ce n'est pas une question sérieuse ou urgente. Il s'agit plutôt de comprendre pourquoi les choses devraient être faites d'une certaine manière plutôt que d'une autre.
Dans la plupart des cas, &&
Et ||
Sont préférés à &
Et |
Car les premiers sont court-circuités, ce qui signifie que l'évaluation est annulée dès que le résultat est clair.
Exemple:
if(CanExecute() && CanSave())
{
}
Si CanExecute
renvoie false
, l'expression complète sera false
, quelle que soit la valeur de retour de CanSave
. Pour cette raison, CanSave
n'est pas exécuté.
Ceci est très pratique dans les circonstances suivantes:
string value;
if(dict.TryGetValue(key, out value) && value.Contains("test"))
{
// Do Something
}
TryGetValue
renvoie false
si la clé fournie n'est pas trouvée dans le dictionnaire. En raison de la nature de court-circuitage de &&
, value.Contains("test")
n'est exécutée que lorsque TryGetValue
renvoie true
et donc value
n'est pas null
. Si vous utilisiez l'opérateur bit à bit ET &
À la place, vous obtiendriez un NullReferenceException
si la clé n'est pas trouvée dans le dictionnaire, car la deuxième partie de l'expression est exécutée dans tous les cas.
Un exemple similaire mais plus simple de ceci est le code suivant (comme mentionné par TJHeuvel):
if(op != null && op.CanExecute())
{
// Do Something
}
CanExecute
n'est exécuté que si op
n'est pas null
. Si op
est null
, la première partie de l'expression (op != null
) Est évaluée à false
et l'évaluation du reste (op.CanExecute()
) est ignoré.
En dehors de cela, techniquement, ils sont également différents:&&
Et ||
Ne peuvent être utilisés que sur bool
tandis que &
Et |
Peuvent être utilisés sur tout type intégral (bool
, int
, long
, sbyte
, ...), car ce sont des opérateurs au niveau du bit. &
Est l'opérateur bit à bit ET et |
Est l'opérateur bit à bit OU opérateur.
Pour être très précis, en C #, ces opérateurs (&
, |
[Et ^
]) Sont appelés "Opérateurs logiques" (voir spécification C # , chapitre 7.11). Il existe plusieurs implémentations de ces opérateurs:
int
, uint
, long
et ulong
, chapitre 7.11.1):&
Est implémenté pour calculer la logique au niveau du bit AND
etc.Expliquer très clairement ce que cela signifie (même si les autres réponses le suggèrent - mais utilisez probablement une terminologie que vous ne comprenez pas).
Le code suivant:
if (a && b)
{
Foo();
}
Est vraiment compilé à ceci:
if (a)
{
if (b)
{
Foo();
}
}
Où le code suivant est compilé exactement tel qu'il est représenté:
if (a & b)
{
Foo();
}
C'est ce qu'on appelle un court-circuit. En général, vous devez toujours utiliser &&
et ||
dans vos conditions.
Marques bonus: Il y a un scénario où vous ne devriez pas. Si vous êtes dans une situation où les performances sont cruciales (et c'est nano-secondes cruciales ) utilisez uniquement les courts-circuits quand vous le devez (par exemple null
vérification) - comme un court-circuit est une branche/saut; ce qui pourrait entraîner une mauvaise prévision de branche sur votre CPU; un &
est beaucoup moins cher que &&
. Il y a aussi un scénario où un court-circuit peut réellement briser la logique - jetez un œil à cette réponse à moi.
Diatribe/Monologue : Concernant la mauvaise prédiction de branche qui ignore le plus béatement. Citant Andy Firth (qui travaille sur des jeux depuis 13 ans): "Cela peut être un niveau inférieur dont les gens pensent qu'ils ont besoin pour aller ... mais ils auraient tort. Comprendre comment le matériel que vous "La programmation pour les succursales traite peut affecter les performances à un degré ÉNORME ... bien plus que la plupart des programmeurs peuvent apprécier re: la mort par mille coupures."
Voici une référence pour les non-croyants. Il est préférable d'exécuter le processus en temps réel/élevé pour atténuer l'effet du planificateur: https://Gist.github.com/1200737
Opérateur logique (||
Et &&
) Par rapport à l'opérateur au niveau du bit (|
Et &
).
La différence la plus cruciale entre un opérateur logique et un opérateur au niveau du bit est qu'un opérateur logique prend deux booléens et produit un booléen tandis qu'un opérateur au niveau du bit prend deux entiers et produit un entier (note : entiers signifie tout type de données intégral, pas seulement int).
Pour être pédant, un opérateur au niveau du bit prend une configuration binaire (par exemple 01101011) et effectue un ET/OU au niveau du bit sur chaque bit. Ainsi, par exemple, si vous avez deux entiers 8 bits:
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)
alors qu'un opérateur logique ne fonctionne que dans bool
:
a = true
b = false
--------------
a && b = false
a || b = true
Deuxièmement, il est souvent possible d'utiliser un opérateur au niveau du bit sur bool puisque true et false sont respectivement équivalents à 1 et 0, et il arrive que si vous traduisez true en 1 et false en 0, puis effectuez une opération au niveau du bit, puis convertissez non nul à vrai et de zéro à faux; il arrive que le résultat soit le même si vous venez d'utiliser l'opérateur logique (vérifiez ceci pour l'exercice).
Une autre distinction importante est également qu'un opérateur logique est court-circuité. Ainsi, dans certains cercles [1], vous voyez souvent des gens faire quelque chose comme ça:
if (person && person.punch()) {
person.doVictoryDance()
}
ce qui se traduit par: "si la personne existe (c.-à-d. n'est pas nulle), essayez de la frapper et si le coup réussit (c.-à-d. retourne vrai), alors faites une danse de la victoire" .
Si vous aviez utilisé un opérateur au niveau du bit à la place, ceci:
if (person & person.punch()) {
person.doVictoryDance()
}
se traduira par: "si la personne existe (c'est-à-dire n'est pas nulle) et que le coup de poing réussit (c'est-à-dire renvoie vrai), alors faites une danse de la victoire" .
Notez que dans l'opérateur logique court-circuité, le code person.punch()
peut ne pas être exécuté du tout si person
est nul. En fait, dans ce cas particulier, le deuxième code produirait une erreur de référence nulle si person
est nul, car il essaie d'appeler person.punch()
, que personne soit nul ou non. Ce comportement consistant à ne pas évaluer l'opérande de droite est appelé court-circuitage.
[1] Certains programmeurs hésiteront à placer un appel de fonction ayant un effet secondaire dans une expression if
, tandis que pour d'autres, c'est un idiome courant et très utile.
Puisqu'un opérateur au niveau du bit fonctionne sur 32 bits à la fois (si vous êtes sur une machine 32 bits), cela peut conduire à un code plus élégant et plus rapide si vous avez besoin de comparer un grand nombre de conditions, par ex.
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`
Faire la même chose avec les opérateurs logiques nécessiterait une quantité maladroite de comparaisons:
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;
Un exemple classique où les modèles de bits et l'opérateur au niveau du bit sont utilisés est dans les autorisations du système de fichiers Unix/Linux.
Dans le cas de:
if (obj != null && obj.Property == true) { }
fonctionnerait comme prévu.
Mais:
if (obj != null & obj.Property == true) { }
pourrait potentiellement lever une exception de référence nulle.
Court et simple:
1 && 2
= vrai
car
1 = vrai (non nul) en C
2 = vrai (non nul) en C
true
ANDS logiquement avec true
pour donner true
.
Mais
1 & 2
= 0 = faux
car
1 = 0001 en binaire
2 = 0010 en binaire
0001 ET au niveau du bit avec 0010 pour donner 0000 = 0 en décimal.
De même pour || et | les opérateurs aussi ...!
Lorsqu'il est utilisé dans une expression logique telle qu'une instruction if &&
préférable, car il cessera d'évaluer les expressions dès que le premier faux résultat sera rencontré. Cela est possible car une fausse valeur entraînera la fausse expression entière. De même (et à nouveau dans les expressions logiques) ||
est préférable car il arrêtera d'évaluer les expressions dès qu'il rencontrera une expression vraie car toute valeur vraie entraînera la véracité de l'expression entière.
Si toutefois les expressions or-ed ou and-ed ensemble ont des effets secondaires et que vous voulez que tout cela se produise à la suite de votre expression (quel que soit le résultat de l'expression logique), alors &
et |
pourrait être utilisé. Inversement, les opérateurs &&
et ||
peuvent être utiles pour se prémunir contre les effets secondaires indésirables (comme un pointeur nul provoquant la levée d'une exception).
Les opérateurs &
et |
peuvent également être utilisés avec des entiers et dans ce cas, ils produisent un résultat entier qui est les deux opérandes and-ed ou or-ed ensemble au niveau du bit. Cela peut être utile lorsque les bits binaires d'une valeur entière sont utilisés comme un tableau de valeurs vraies et fausses. Pour tester si un certain bit est activé ou désactivé, un masque de bits est bit à bit et édité avec la valeur. Pour activer un peu, le même masque peut être bit à bit ou édité avec la valeur. Enfin, pour désactiver un peu, le complément au niveau du bit (en utilisant ~
) d'un masque est au niveau du bit et édité avec la valeur.
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
Dans les langages autres que C #, il faut faire attention aux modes logique et bit à bit de & et |. Dans le code ci-dessus, l'expression conditionnelle de l'instruction if
(a & 4) != 0
est un moyen sûr d'exprimer cette condition, mais dans de nombreux langages de type C, les instructions conditionnelles peuvent simplement traiter les valeurs entières nulles comme fausses et non -les valeurs entières nulles sont vraies. (La raison en est liée aux instructions du processeur de branche conditionnel disponibles et à leur relation avec l'indicateur zéro qui est mis à jour après chaque opération entière.) Ainsi, le test de l'instruction ìf
pour zéro peut être supprimé et la condition pourrait être raccourci en (a & 4)
.
Cela pourrait causer de la confusion et peut-être même des problèmes lorsque les expressions combinées à l'aide des valeurs de retour au niveau du bit et de l'opérateur qui n'ont pas de bits alignés. Considérez l'exemple suivant où les effets secondaires de deux fonctions sont souhaités, avant de vérifier qu'ils ont tous deux réussi (comme défini par eux en retournant une valeur non nulle):
if (foo() & bar()) {
// do something
}
En C, si foo()
renvoie 1 et bar()
renvoie 2, le "quelque chose" ne sera pas fait car 1 & 2
est nul.
C # requiert des instructions conditionnelles telles que if
pour avoir une extension booléenne, et le langage ne permet pas de convertir une valeur entière en valeur booléenne. Le code ci-dessus générerait donc des erreurs de compilation. Il serait plus correctement exprimé comme suit:
if (foo() != 0 & bar() != 0) {
// do something
}
OK, à première vue
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);
produire la même réponse. Cependant, comme vous l'avez montré, si vous avez une question plus complexe, alors:
if (a and b and c and d) ..
Si a
n'est pas vrai et peut-être que b
est une fonction où elle doit s'éteindre, se connecter à quelque chose, obtenir ceci, le faire, prendre une décision .. pourquoi s'embêter? Perte de temps, vous savez qu'il a déjà échoué. Pourquoi faire démarrer la machine et faire un travail inutile supplémentaire?
J'ai toujours utilisé &&
parce que je mets le plus de chances d'échouer en premier, ergo, moins de calculs avant de passer quand il n'y a pas de point. S'il n'y a aucun moyen de prédire des choix moins probables, comme si vous avez un booléen pour limiter la sortie des données, quelque chose comme:
if (limit && !MyDictionary.ContainsKey("name"))
continue;
Si ce n'est pas limit
, ne vous embêtez pas à vérifier la clé, ce qui pourrait prendre plus de temps ..
Opérateurs C # devrait expliquer pourquoi:
Avoir essentiellement deux &
ou |
signifie que c'est un conditionnel plutôt qu'un logique, vous pouvez donc faire la différence entre les deux.
& Operator a un exemple d'utilisation d'un &
.
&&
est la version en court-circuit de &
.
Si nous évaluons false & true
, nous savons déjà en regardant le premier argument que le résultat sera faux. Le &&
la version de l'opérateur retournera un résultat dès que possible, plutôt que d'évaluer l'expression entière. Il existe également une version similaire du |
opérateur, ||
.
if (list.Count() > 14 && list[14] == "foo")
est sécurisé
if (list.Count() > 14 & list[14] == "foo")
planterait si la liste n'a pas la bonne taille.
Si vous êtes un ancien programmeur C, soyez prudent . C # m'a vraiment surpris.
MSDN dit pour le |
opérateur:
Binaire | les opérateurs sont prédéfinis pour les types intégraux et bool . Pour les types intégraux, | calcule le bit OR de ses opérandes. Pour les opérandes booléens, | calcule la logique OR de ses opérandes; autrement dit, le résultat est faux si et seulement si ses deux opérandes sont faux.
(L'accent est sur moi.) Les types booléens sont traités spécialement, et dans ce contexte, la question commence seulement à avoir un sens, et la différence est, comme d'autres l'ont déjà expliqué dans leurs réponses:
&&
et||
sont en court-circuit.&
et|
évalue les deux opérandes.
et ce qui est préférable dépend de beaucoup de choses comme les effets secondaires, les performances et la lisibilité du code, mais généralement les opérateurs de court-circuit sont préférables aussi parce qu'ils sont mieux compris par des personnes ayant des antécédents similaires comme moi.
La raison est: je dirais comme ceci: puisqu'il n'y a pas de vrai type booléen en C, vous pouvez utiliser l'opérateur au niveau du bit |
et faire évaluer son résultat comme véridique ou falsifié dans une condition if. Mais c'est la mauvaise attitude pour C #, car il existe déjà un cas particulier pour les types booléens.
La façon la plus rapide (et légèrement simplifiée) d'expliquer cela aux personnes qui n'ont PAS BESOIN de connaître les opérations exactes du code lors de cette opération est
&& vérifie chacune de ces conditions Jusqu'à il trouve un faux et renvoie le résultat entier comme faux
vérifie chacune de ces conditions Jusqu'à il trouve un vrai et renvoie le résultat entier comme vrai.
& fait MATHS sur la base des DEUX/TOUTES les conditions et gère le résultat.
fait MATHS sur la base des DEUX/TOUTES les conditions et gère le résultat.
Je n'ai jamais rencontré de point où j'ai dû utiliser & ou dans une instruction if. Je l'utilise principalement pour découper des valeurs hexadécimales dans ses couleurs de composants en utilisant le décalage au niveau du bit.
PAR EXEMPLE:
r = fullvalue >> 0xFF & 0xFF;
g = fullvalue >> 0xF & 0xFF;
b = fullvalue & 0xFF;
Dans cette opération, "& 0xFF" force à ne regarder que la valeur binaire. Je n'ai pas encore personnellement trouvé d'utilisation pour .
Simplement,
if exp1 && exp2
si exp1 est flase
ne cochez pas exp2
mais
if exp1 & exp2
si exp1 est false
ou true
cochez exp2
et rarement les gens utilisent &
car ils veulent rarement vérifier exp2 si exp1 est false
&& et & signifient deux choses très différentes et vous donnent deux réponses différentes.
1 && 2
donne 1 ("vrai")1 & 2
donne 0 ("faux")
&&
est un opérateur logique - cela signifie "vrai si les deux opérandes sont vrais"&
est une comparaison au niveau du bit. Cela signifie "dites-moi quels bits sont définis dans les deux opérandes"
C'est important, parce que si le coût de l'évaluation de bool2 (par exemple) est élevé mais bool1 est faux, alors vous vous êtes épargné un peu de calcul en utilisant && sur &
Car &&
et ||
sont utilisés pour contrôle de flux comme if/else
sont. Il ne s'agit pas toujours de conditions. Il est parfaitement raisonnable d'écrire comme une instruction, et non comme if
ou while
conditionnel, comme suit:
a() && b() && c() && d();
ou même
w() || x() || y() || z();
Ce n'est pas seulement que ce sont plus faciles à taper que l'équivalent if/else
versions; ils sont également beaucoup plus faciles à lire et à comprendre.