Pourquoi «&&» et pas «&»?

135

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*/ }, bool1doit être vrai pour qu'il teste bool2ce 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.

Dani
la source
55
& et && | et || sont des opérateurs complètement différents
Felice Pollano
3
Retagged, car cela ne s'applique pas seulement à C #
Jonathan Dickinson
12
Inversé, car les réponses sont déjà spécifiques à C # et le fonctionnement interne peut différer un peu dans d'autres langages qui ont généralement le même concept.
Daniel Hilgarth
8
@Felice: Ils sont différents, mais à peine complètement différents. Ils sont en fait très similaires: x & yet x && ydonneront 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 in x & y, y est toujours évalué.
Joren
1
@slawekin: Je suggère de lire les réponses. Certains écrivent abondamment sur les différences de performances. La réponse pourrait cependant vous surprendre.
Abel le

Réponses:

183

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:

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

Si CanExecuterenvoie false, l'expression complète sera false, quelle que soit la valeur de retour de CanSave. Pour cette raison, CanSaven'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
}

TryGetValuerenvoie falsesi 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 lorsque TryGetValueretourne trueet valuene l'est donc pas null. Si vous utilisiez& plutôt l' opérateur binaire AND , vous obtiendrez a NullReferenceExceptionsi 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
}

CanExecuten'est exécuté que si ce opn'est pas le cas null. Si opc'est le cas null, la première partie de l'expression ( op != null) est évaluée à falseet 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 sur boolalors &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:

  1. Pour les entiers ( int, uint, longet ulong, 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 , ANDetc.
  2. Pour les énumérations (chapitre 7.11.2):
    Elles sont implémentées pour effectuer l'opération logique du type sous-jacent de l'énumération.
  3. Pour les bools et les bools nullables (chapitres 7.11.3 et 7.11.4):
    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.
Daniel Hilgarth
la source
31
Cela peut également être pratique pour vérifier si quelque chose est nul. Par exemple: 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.
TJHeuvel
2
@TJHeuvel: C'est essentiellement la même utilisation que j'ai décrite avec mon TryGetValueexemple. Mais oui, c'est un autre bon exemple de cela.
Daniel Hilgarth
4
Bonne réponse. Vous devriez peut-être également ajouter un exemple de la façon dont &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.
Zabba
81

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:

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 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, nullvé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.

  • Les développeurs de jeux (et d'autres travaillant dans des conditions extrêmes en temps réel) vont jusqu'à restructurer leur logique pour mieux s'adapter au prédicteur. Il existe également des preuves de cela dans le code décompilé de mscorlib.
  • Ce n'est pas parce que .NET vous protège de ce type de chose que ce n'est pas important. Une erreur de prédiction de branche coûte horriblement cher à 60 Hz; ou à 10 000 requêtes / seconde.
  • Intel n'aurait pas d'outils pour identifier l'emplacement des prédictions erronées, ni Windows n'aurait un compteur de performance pour cela, ni un mot pour le décrire, si ce n'était pas un problème.
  • L'ignorance des niveaux inférieurs et de l'architecture ne fait pas tort à quelqu'un qui en est conscient.
  • Essayez toujours de comprendre les limites du matériel sur lequel vous travaillez.

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

Jonathan Dickinson
la source
7
À propos des «primes»: Nous connaissons tous le bien qui résulte d'une optimisation prématurée. :)
un CVn le
6
@Michael - c'est pourquoi «nano-secondes cruciales» est en gras :). Les développeurs de jeux AAA s'inquiètent généralement de ce genre de choses - et vous ne savez jamais qui lira les réponses; il est donc toujours préférable de documenter même les cas limites / extrêmes.
Jonathan Dickinson
1
Cette marque de bonus est-elle valide pour C #? J'aurais pensé que non, comme MSIL est interprété, à moins que l'expression ne soit compilée jusqu'au code machine.
Jeremy McGee
7
@Jeremy MSIL n'est pas interprété.
Jonathan Dickinson
2
@TheD revérifier la réponse - J'ai ajouté un monologue expliquant pourquoi vous DEVRIEZ vous inquiéter à ce sujet. Et, FYI, se (x && y)traduit par LOAD x; BRANCH_FALSE; LOAD y; BRANCH_FALSE;où se (x & y)traduit par LOAD x; LOAD y; AND; BRANCH_FALSE;. Une succursale contre deux.
Jonathan Dickinson
68

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:

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 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:

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

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:

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 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'il personest nul. En fait, dans ce cas particulier, le deuxième code produirait une erreur de référence nulle s'il personest nul, car il essaie d'appeler person.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 ifexpression, 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

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 de même avec les opérateurs logiques exigerait une quantité délicate 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 binaires et l'opérateur binaire sont utilisés est celui des permissions du système de fichiers Unix / Linux.

Mensonge Ryan
la source
3
+1 pour le côté affecte le problème. Surpris qu'il n'ait pas été mentionné plus tôt
Conrad Frix
3
l'exemple semble un peu violent, mais semble que d'autres réponses se concentrent trop sur les courts-circuits et pas assez sur la différence entre le fonctionnement sur des entiers et sur des booléens.
R0MANARMY
La fonction doit être comprise avant que les détails de mise en œuvre (court-circuit / effets secondaires) n'entrent en jeu. Heureux que vous ayez résolu la principale différence entre la logique booléenne et la logique entière, et non le court-circuit.
Abel le
@ROMANARMY - violent, j'adore l'ironie donnée à votre surnom. Nice work
brumScouse
8

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.

gras
la source
2

Bref et simple:

1 && 2= vrai
car
1 = vrai (non nul) en C
2 = vrai (non nul) en C

trueANDS logiquement avec truepour donner true.

Mais

1 & 2= 0 = faux
car
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 ...!

Shrey
la source
2
-1: Nous parlons de C # ici ... 1 && 2est illégal en C #
Daniel Hilgarth
Mais c'est un exemple extrêmement important qui explique pourquoi vous ne pouvez pas simplement échanger & et && (ce que beaucoup de gens semblent penser).
bobobobo
1

&&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, ||.

mdm
la source
1
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.

jalf
la source
Je ne peux pas imaginer que quelqu'un puisse écrire "if (list.Count ()> 14 & list [14] ==" foo ")" au lieu de "if (list.Count ()> 14 && list [14] ==" toto ")". & tout simplement et naturellement ne peut pas être utilisé pour && dans ce cas même si & est sûrement sûr (list [1] par exemple).
Tien Do
1

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&.

Stuart Thomson
la source
Les deux liens sont (effectivement) rompus (redirige vers «Visual Studio 2005 Retired documentation» ).
Peter Mortensen
1

OK, sur la valeur nominale

    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 ce an'est pas vrai et c'est peut b- ê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:

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

Si ce n'est pas le cas limit, ne cherchez pas la clé, cela peut prendre plus de temps.

BugFinder
la source
1

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.

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 avec les modes logique et bit à bit de & et |. Dans le code ci-dessus, l' ifexpression conditionnelle de l' instruction (a & 4) != 0est 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):

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

En C, si foo()retourne 1 et bar()retourne 2, le "quelque chose" ne sera pas fait car il 1 & 2est nul.

C # nécessite des instructions conditionnelles comme ifavoir 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:

if (foo() != 0 & bar() != 0) {
    // do something
}
graza
la source
1

Si vous êtes un programmeur C à l'ancienne, soyez prudent . C # m'a vraiment surpris.

MSDN dit à l' |opérateur:

Binaire | Les opérateurs sont prédéfinis pour les types intégraux et booléens . Pour les types intégraux, | calcule le OU au niveau du bit de ses opérandes. Pour les opérandes booléens, | calcule le OU logique de ses opérandes; autrement dit, le résultat est faux si et seulement si ses deux opérandes sont faux.

(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 ||sont en court-circuit. &et |évaluez 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 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.

nalply
la source
0

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 &

dépensier
la source
0

Parce que &&et ||sont utilisés pour le contrôle de flux, tout comme le if/elsesont. Ce n'est pas toujours une question de condition. Il est parfaitement raisonnable d'écrire comme une déclaration, et non comme un ifou un whileconditionnel, ce qui suit:

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

ou même

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

Ce n'est pas seulement que ce sont plus faciles à taper que les if/elseversions équivalentes ; ils sont également beaucoup plus faciles à lire et à comprendre.

tchrist
la source
0

&& et & signifient deux choses très différentes et vous donnent deux réponses différentes.

1 && 2donne 1 ("vrai")
1 & 2donne 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"

Tylerl
la source
2
La question concerne C #. En C #, il n'y a aucun moyen de convertir un nombre en booléen, donc 0 n'est pas «faux» et différent de zéro n'est pas «vrai»; il n'y a tout simplement pas d'équivalence.
Nate CK
Pour convertir un nombre en booléen, d'une manière que 1 signifie vrai et 0 signifie faux, dites "n! = 0" (je suppose ... je ne suis pas vraiment familier avec C #). En fait, je voulais retirer ce commentaire car il n'est pas bien documenté et je ne pense pas que ce soit utile ou vraiment pertinent pour le commentaire précédent maintenant que j'y pense plus, mais j'ai accidentellement appuyé sur Entrée et maintenant je ne pense pas que je peux annulez-le, alors allez-y, pour ce que ça vaut :-)
Don Hatch
1 && 2donne une erreur du compilateur: "Erreur 4 L'opérateur '&&' ne peut pas être appliqué aux opérandes de type 'int' et 'int'"
Peter Mortensen
0

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:

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

Dans cette opération "& 0xFF" oblige à ne regarder que la valeur binaire. Je n'ai personnellement pas trouvé d'utilisation pour | pourtant cependant.

WORMSS
la source
0

Simplement,

if exp1 && exp2

si exp1 est flase ne pas vérifier exp2

mais

if exp1 & exp2

si exp1 est falseOu true vérifier exp2

et rarement les gens utilisent &parce qu'ils veulent rarement vérifier exp2 si exp1 estfalse

Fady
la source