Pourquoi est-il &&
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
ce qui doit être vrai avant de passer à bool3
, etc. Si j'avais utilisé un seul à la &
place, il n'y a pas d'ordre de test même si tous doivent être vrais. passer à la ligne suivante, alors pourquoi est-ce important de toute façon?
Remarque: je tiens à souligner que je suis l'équivalent en programmation d'un enfant en bas âge et que ce n'est pas une question sérieuse ou urgente. Il s'agit plus de comprendre pourquoi les choses devraient être faites d'une certaine manière plutôt que d'une autre.
x & y
etx && y
donneront toujours le même résultat si x et y sont des expressions de type booléen. En fait, la seule différence dans ce cas semble être que inx & y
, y est toujours évalué.Réponses:
Dans la plupart des cas,
&&
et||
sont préférés à&
et|
parce que les premiers sont court-circuités, ce qui signifie que l'évaluation est annulée dès que le résultat est clair.Exemple:
Si
CanExecute
renvoiefalse
, l'expression complète serafalse
, quelle que soit la valeur de retour deCanSave
. Pour cette raison,CanSave
n'est pas exécuté.Ceci est très pratique dans les circonstances suivantes:
TryGetValue
renvoiefalse
si la clé fournie n'est pas trouvée dans le dictionnaire. En raison de la nature de court-circuit de&&
,value.Contains("test")
n'est exécuté que lorsqueTryGetValue
retournetrue
etvalue
ne l'est donc pasnull
. Si vous utilisiez&
plutôt l' opérateur binaire AND , vous obtiendrez aNullReferenceException
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):
CanExecute
n'est exécuté que si ceop
n'est pas le casnull
. Siop
c'est le casnull
, la première partie de l'expression (op != null
) est évaluée àfalse
et l'évaluation de rest (op.CanExecute()
) est ignorée.En dehors de cela, ils sont techniquement différents aussi:
&&
et||
ne peuvent être utilisés surbool
alors&
et|
peut être utilisé sur tout type intégral (bool
,int
,long
,sbyte
, ...), parce qu'ils sont des opérateurs au niveau du bit.&
est l' opérateur AND au niveau du bit et|
est l' opérateur OR au niveau du bit .Pour être très précis, en C #, ces opérateurs (
&
,|
[et^
]) sont appelés "Opérateurs logiques" (voir la spécification C # , chapitre 7.11). Il existe plusieurs implémentations de ces opérateurs:int
,uint
,long
etulong
, chapitre 7.11.1):Ils sont mis en œuvre pour calculer le résultat binaire des opérandes et l'opérateur, à savoir
&
est de mettre en œuvre pour calculer la logique binaire ,AND
etc.Elles sont implémentées pour effectuer l'opération logique du type sous-jacent de l'énumération.
Le résultat n'est pas calculé en utilisant des calculs au niveau du bit. Le résultat est essentiellement recherché en fonction des valeurs des deux opérandes, car le nombre de possibilités est si petit.
Étant donné que les deux valeurs sont utilisées pour la recherche, cette implémentation n'est pas un court-circuit.
la source
if(op != null && op.CanExecute())
. Étant donné que la deuxième cause n'est pas évaluée lorsque la première n'est pas vraie, cela est valide.TryGetValue
exemple. Mais oui, c'est un autre bon exemple de cela.&
ou|
est utilisé avec des arguments non booléens (c'est-à-dire ce que font les opérateurs) pour le bénéfice de toutes les personnes nouvelles.Pour 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:
Est vraiment compilé à ceci:
Où le code suivant est compilé exactement tel qu'il est représenté:
C'est ce qu'on appelle un court-circuit. En général, vous devez toujours utiliser
&&
et||
dans vos conditions.Marques bonus: il existe un scénario dans lequel vous ne devriez pas. Si vous êtes dans une situation où les performances sont cruciales (et c'est crucial en nanosecondes ), n'utilisez le court-circuit que lorsque vous le devez (par exemple,
null
vérification) - car un court-circuit est une branche / saut; ce qui pourrait entraîner une erreur de prédiction de branche sur votre CPU; un&
est beaucoup moins cher que&&
. Il existe également un scénario dans lequel un court-circuit peut en fait interrompre la logique - jetez un œil à ma réponse .Diatribe / Monologue : En ce qui concerne la mauvaise prédiction de branche que le plus béatement ignorer. Citant Andy Firth (qui travaille sur des jeux depuis 13 ans): "C'est peut-être un niveau inférieur auquel les gens pensent devoir aller ... mais ils auraient tort. Comprendre comment le matériel que vous programmez pour traiter les branches peut affectent les performances à un degré ÉNORME ... bien plus que la plupart des programmeurs peuvent apprécier re: la mort par mille coupures.
Voici un repère pour les non-croyants. Il est préférable d'exécuter le processus en RealTime / High pour atténuer l'effet du planificateur: https://gist.github.com/1200737
la source
(x && y)
traduit parLOAD x; BRANCH_FALSE; LOAD y; BRANCH_FALSE;
où se(x & y)
traduit parLOAD x; LOAD y; AND; BRANCH_FALSE;
. Une succursale contre deux.Opérateur logique (
||
et&&
) vs opérateur binaire (|
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: les entiers signifie tout type de données intégral, pas seulement int).
Pour être pédant, un opérateur au niveau du bit prend un modèle de bits (par exemple 01101011) et effectue un ET / OU au niveau du bit sur chaque bits. Ainsi, par exemple, si vous avez deux entiers 8 bits:
alors qu'un opérateur logique ne fonctionne que dans
bool
:Deuxièmement, il est souvent possible d'utiliser un opérateur binaire sur booléen puisque vrai et faux équivalent respectivement à 1 et 0, et il arrive que si vous traduisez vrai en 1 et faux en 0, effectuez une opération binaire, puis convertissez une valeur non nulle à vrai et 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 ceci:
ce qui se traduit par: "si la personne existe (c'est-à-dire qu'elle n'est pas nulle), essayez de la frapper, et si le coup de poing réussit (c'est-à-dire qu'elle retourne vrai), alors faites une danse de la victoire" .
Si vous aviez utilisé un opérateur au niveau du bit à la place, ceci:
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 retourne vrai), alors faire une danse de la victoire" .
Notez que dans l'opérateur logique court-circuité, le
person.punch()
code peut ne pas être exécuté du tout s'ilperson
est nul. En fait, dans ce cas particulier, le deuxième code produirait une erreur de référence nulle s'ilperson
est nul, car il essaie d'appelerperson.punch()
, que personne soit nul ou non. Ce comportement consistant à ne pas évaluer le bon opérande est appelé court-circuit .[1] Certains programmeurs hésiteront à mettre un appel de fonction qui a un effet secondaire dans une
if
expression, tandis que pour d'autres, c'est un idiome commun 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 exemple
Faire de même avec les opérateurs logiques exigerait une quantité délicate de comparaisons:
Un exemple classique où les modèles binaires et l'opérateur binaire sont utilisés est celui des permissions du système de fichiers Unix / Linux.
la source
Dans le cas de:
fonctionnerait comme prévu.
Mais:
pourrait potentiellement lever une exception de référence nulle.
la source
Bref et simple:
1 && 2
= vraicar
1 = vrai (non nul) en C
2 = vrai (non nul) en C
true
ANDS logiquement avectrue
pour donnertrue
.Mais
1 & 2
= 0 = fauxcar
1 = 0001 en binaire
2 = 0010 en binaire
0001 ETs au niveau du bit avec 0010 pour donner 0000 = 0 en décimal.
De même pour || et | les opérateurs aussi ...!
la source
1 && 2
est illégal en C #&&
est la version court-circuit de&
.Si nous évaluons
false & true
, nous savons déjà en regardant le premier argument que le résultat sera faux. La&&
version de l'opérateur renverra un résultat dès qu'elle le pourra, plutôt que d'évaluer l'expression entière. Il y a aussi une verion similaire de l'|
opérateur,||
.la source
est sécurisé
planterait si la liste n'a pas la bonne taille.
la source
Opérateurs C # doivent expliquer pourquoi:
Essentiellement, avoir deux
&
ou|
signifie qu'il s'agit d'un conditionnel plutôt que d'un logique, vous pouvez donc faire la différence entre les deux.& L'opérateur a un exemple d'utilisation d'un
&
.la source
OK, sur la valeur nominale
produire la même réponse. Cependant, comme vous l'avez montré, si vous avez une question plus complexe, alors:
Si ce
a
n'est pas vrai et c'est peutb
- être une fonction où cela doit se déclencher, se connecter à quelque chose, obtenir ceci, faire cela, prendre une décision .. pourquoi s'embêter? Perte de temps, toi savez que c'est déjà un échec. Pourquoi faire démarrer la machine et effectuer un travail supplémentaire inutile?Je l'ai toujours utilisé
&&
parce que je mets le plus susceptible d'échouer en premier, ergo, moins de calculs avant de passer à autre chose quand ça ne sert à rien. 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 de données, quelque chose comme:Si ce n'est pas le cas
limit
, ne cherchez pas la clé, cela peut prendre plus de temps.la source
Lorsqu'il est utilisé dans une expression logique telle qu'une instruction if, c'est
&&
préférable car il arrêtera d'évaluer les expressions dès que le premier résultat faux est rencontré. Cela est possible car une valeur fausse rendra toute l'expression fausse. De même (et encore dans les expressions logiques)||
est préférable car il arrêtera d'évaluer les expressions dès qu'il rencontre une expression vraie car toute valeur vraie fera que l'expression entière sera vraie.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 protéger contre les effets secondaires indésirables (tels qu'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 et-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 au niveau du bit et modifié avec la valeur. Pour activer un peu, le même masque peut être ou-ed au niveau du bit avec la valeur. Enfin, pour désactiver un peu, le complément au niveau du bit (utilisant~
) d'un masque est au niveau du bit et associé à la valeur.Dans les langages autres que C #, il faut faire attention avec les modes logique et bit à bit de & et |. Dans le code ci-dessus, l'
if
expression conditionnelle de l' instruction(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 les valeurs entières non nulles comme vraies. (La raison en est liée aux instructions du processeur de branchement conditionnel disponibles et à leur relation avec l'indicateur zéro qui est mis à jour après chaque opération sur un entier.) Ainsiìf
, le test de zéro de l' instruction peut être supprimé et la condition peut être raccourcie(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 de bit et d'opérateur qui n'ont pas de bits alignés. Prenons l'exemple suivant où les effets secondaires de deux fonctions sont souhaités, avant de vérifier qu'elles ont toutes deux réussi (comme défini par elles en retournant une valeur non nulle):
En C, si
foo()
retourne 1 etbar()
retourne 2, le "quelque chose" ne sera pas fait car il1 & 2
est nul.C # nécessite des instructions conditionnelles comme
if
avoir un oeprand booléen, et le langage ne permet pas à une valeur entière d'être convertie en valeur booléenne. Ainsi, le code ci-dessus générerait des erreurs de compilation. Il serait plus correctement exprimé comme suit:la source
Si vous êtes un programmeur C à l'ancienne, soyez prudent . C # m'a vraiment surpris.
MSDN dit à l'
|
opérateur:(Je souligne.) Les types booléens sont traités spécialement, et dans ce contexte, la question ne fait que commencer à avoir un sens, et la différence est, comme d'autres l'ont déjà expliqué dans leurs réponses:
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 une expérience similaire comme moi.
La raison est la suivante: je voudrais argumenter 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 vrai ou faux 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 source
C'est important, car si le coût de l'évaluation de bool2 (par exemple) est élevé mais que bool1 est faux, alors vous vous êtes épargné un peu de calcul en utilisant && over &
la source
Parce que
&&
et||
sont utilisés pour le contrôle de flux, tout comme leif/else
sont. Ce n'est pas toujours une question de condition. Il est parfaitement raisonnable d'écrire comme une déclaration, et non comme unif
ou unwhile
conditionnel, ce qui suit:ou même
Ce n'est pas seulement que ce sont plus faciles à taper que les
if/else
versions équivalentes ; ils sont également beaucoup plus faciles à lire et à comprendre.la source
&& 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"la source
1 && 2
donne une erreur du compilateur: "Erreur 4 L'opérateur '&&' ne peut pas être appliqué aux opérandes de type 'int' et 'int'"Le moyen le plus rapide (et légèrement simplifié) 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'à ce que ce qu'il trouve un faux et renvoie le résultat entier comme faux
|| vérifie chacune de ces conditions jusqu'à ce que ce qu'il trouve un vrai et renvoie le résultat entier comme vrai.
& fait des MATHS basés sur LES DEUX / TOUTES les conditions et traite le résultat.
|fait des MATHS basés sur LES DEUX / TOUTES les conditions et s'occupe du résultat.
Je n'ai jamais rencontré de point où j'ai eu besoin d'utiliser & ou |dans une instruction if. Je l'utilise principalement pour découper les valeurs hexadécimales en couleurs de ses composants en utilisant le décalage au niveau du bit.
PAR EXEMPLE:
Dans cette opération "& 0xFF" oblige à ne regarder que la valeur binaire. Je n'ai personnellement pas trouvé d'utilisation pour | pourtant cependant.
la source
Simplement,
if exp1 && exp2
si exp1 est
flase
ne pas vérifier exp2mais
if exp1 & exp2
si exp1 est
false
Outrue
vérifier exp2et rarement les gens utilisent
&
parce qu'ils veulent rarement vérifier exp2 si exp1 estfalse
la source