Pourquoi y a-t-il à la fois un court-circuit OU ainsi qu'une variation non court-circuitée de cet opérateur en C #?

9

Périodiquement, je me pose des questions à ce sujet:

Le OU court-circuit retournerait toujours la même valeur que l'opérateur OU court-circuité le ferait?

Je m'attends à ce que le court-circuit OR soit toujours évalué plus rapidement. Alors, l'opérateur OR non court-circuité a-t-il été inclus dans le langage C # pour plus de cohérence?

Qu'est-ce que j'ai raté?

CarneyCode
la source
9
Même valeur de retour - oui. Mêmes effets secondaires - non.
Job
2
Supposons que f()déclenche une exception, considérons true || f()et true | f(). Voyez-vous la différence? La première expression est évaluée true, l'évaluation de la dernière entraîne la levée d'une exception.
scarfridge

Réponses:

25

Les deux opérandes étaient destinés à des choses différentes, provenant de C qui n'avait pas de type booléen. La version court-circuit || ne fonctionne qu'avec des booléens, tandis que la version non-court-circuit | fonctionne avec des types intégraux, effectuant un bit ou. Il s'est avéré que cela fonctionnait comme une opération logique sans court-circuit pour les booléens qui sont représentés par un seul bit, soit 0 ou 1.

http://en.wikibooks.org/wiki/C_Sharp_Programming/Operators#Logical

Sécurise
la source
10
+1 pour le lien. Quand j'ai lu cette question, je me suis dit "quoi?" depuis afaik, le nom officiel de ceux-ci est logique et au niveau du bit ou, et non pas un court-circuit et un court-circuit qui se trouvent être des effets secondaires de leur fonctionnement.
stijn
1
+1 Je vois "fonctionne uniquement sur des opérandes booléens" - Je n'ai peut-être pas remarqué la différence parce que j'utilise de toute façon des expressions qui sont évaluées en booléen
CarneyCode
2
J'ai dû vérifier, mais il est en fait vrai que l' |opérateur, lorsqu'il est appliqué à deux valeurs booléennes, est compilé dans le même oropérateur en CIL que lorsqu'il est appliqué à deux valeurs entières - contrairement à ||ce qui est compilé dans CIL en utilisant brtruepour un conditionnel saut.
quentin-star du
13

|(ou bit à bit) ne doit pas être en court-circuit pour des types comme int. En effet, dans presque tous les cas, vous devez calculer les deux côtés d'une |expression pour calculer le résultat correct. Par exemple, quel est le résultat de 7 | f(x)?

Si f(x)n'est pas évalué, vous ne pouvez pas le dire.

De plus, il serait incohérent de faire court-circuiter cet opérateur bool, alors qu'il ne l'est pas int. Soit dit en passant, je pense que je n'ai jamais utilisé |de comparaisons logiques intentionnellement, trouvant qu'il est très gênant de parler d' |opérateur logique.

|| cependant, c'est pour les comparaisons logiques, où l'évaluation des courts-circuits fonctionne bien.

La même chose est valable même pour C et C ++, où l'origine de ces opérateurs est.

Doc Brown
la source
5

C'est correct, l'opérateur OU en court-circuit (||) renverra toujours la même valeur que l'opérateur OU non-court-circuit (|). (*)

Cependant, si le premier opérande est vrai, l'opérateur de court-circuit ne provoquera pas d'évaluation du deuxième opérande, tandis que l'opérateur de non-court-circuit provoquera toujours l'évaluation des deux opérandes. Cela peut avoir un impact sur les performances et parfois des effets secondaires.

Donc, il y a une utilisation pour les deux: si vous vous souciez de la performance et que l'évaluation du deuxième opérande ne produit aucun effet secondaire (ou si vous ne vous en souciez pas), alors utilisez par tous les moyens l'opérateur de court-circuit . Mais si, pour une raison quelconque, vous avez besoin des effets secondaires du deuxième opérande, vous devez utiliser l'opérateur non-court-circuit.

Un exemple où vous devez utiliser l'opérateur de non-court-circuit:

if( write_customer_to_database() != SUCCESS |
    write_supplier_to_database() != SUCCESS |
    write_order_to_database() != SUCCESS )
{
    transaction_rollback();
}

(*) À l'exception d'un scénario vraiment perverti où l'évaluation du premier opérande à faux provoque par effet secondaire le deuxième opérande à évaluer vrai au lieu de faux.

Mike Nakis
la source
2
+1 Mais j'aimerais ajouter qu'en plus d'utiliser des fonctions pour indiquer le succès ou l'échec (comme dans votre exemple): les fonctions avec des effets secondaires devraient généralement être évitées ...
Marjan Venema
1
Oui, c'est probablement pourquoi je n'ai jamais utilisé aucun des opérateurs non-court-circuit jusqu'à aujourd'hui, et les chances sont minces que je ne le fasse jamais.
Mike Nakis
Cet exemple dépend des opérandes évalués dans un ordre strict de gauche à droite. C # le garantit, mais de nombreux langages similaires (y compris C et C ++) ne le font pas.
Keith Thompson
L'évaluation des courts-circuits ne serait-elle pas logique pour cet exemple particulier, puisque toutes les transactions seront annulées si l'une d'entre elles échoue? (Je fais quelques hypothèses sur la sémantique des appels.)
Keith Thompson
@KeithThompson vous avez raison, une évaluation sans court-circuit entraînera un traitement inutile, et en ce sens, l'exemple est un peu boiteux, mais il n'est pas si boiteux qu'il mérite d'être modifié, car la vérité demeure qu'avec une évaluation en court-circuit si write_customer_to_database () réussit, alors le reste des fonctions d'écriture _... ne sera jamais appelé, et il vaut mieux travailler correctement en cas de succès, que d'effectuer des opérations inutiles en cas d'échec.
Mike Nakis
4

Il y aurait 2 raisons d'utiliser la variante sans court-circuit:

  1. garder les effets secondaires (mais c'est plus clair à coder avec une variable temporaire)

  2. déjouer les attaques de synchronisation en évitant la fourchette dans le court-circuit (cela peut même améliorer l'efficacité d'exécution si le deuxième opérande est une variable mais c'est une micro-optimisation que le compilateur peut faire de toute façon)

monstre à cliquet
la source
C'est la seule bonne raison fournie, je n'ai aucune idée pourquoi ce n'est pas la réponse acceptée avec les votes les plus élevés ...
Behrooz
2

si la clause de droite de l'opérateur a un effet secondaire, et le programmeur voulait qu'il souhaite que les deux effets secondaires se produisent avant de vérifier leur valeur de retour.

Lie Ryan
la source
5
Eek. Veuillez ne pas coder comme ça… si vous comptez sur des effets secondaires, avez deux expressions différentes, attribuez les résultats et testez-les ensuite .
Konrad Rudolph