Pourquoi voit-on souvent «null! = Variable» au lieu de «variable! = Null» en C #?

103

En c #, y a-t-il une différence de vitesse d'exécution pour l'ordre dans lequel vous énoncez la condition?

if (null != variable) ...
if (variable != null) ...

Depuis récemment, j'ai vu le premier assez souvent, et il a retenu mon attention puisque j'étais habitué au second.

S'il n'y a pas de différence, quel est l'avantage du premier?

mr_georg
la source
Certains langages de programmation prennent en charge l'initialisation de la valeur dans la condition if. Dans de tels langages, si nous voulons comparer LValue avec RValue, il est recommandé d'utiliser littéral à gauche comme if (1 == i). donc accidentellement si nous mettons = plutôt == alors cela donne une erreur de compilation.
Jugal Panchal

Réponses:

161

C'est un retard de C. En C, si vous utilisez un mauvais compilateur ou si vous n'avez pas d'avertissements suffisamment élevés, cela se compilera sans aucun avertissement (et c'est en effet du code légal):

// Probably wrong
if (x = 5)

quand tu voulais probablement dire

if (x == 5)

Vous pouvez contourner ce problème en C en faisant:

if (5 == x)

Une faute de frappe ici entraînera un code invalide.

Maintenant, en C #, tout est piffle. À moins que vous ne compariez deux valeurs booléennes (ce qui est rare, IME), vous pouvez écrire le code le plus lisible, car une instruction "if" nécessite une expression booléenne pour commencer, et le type de " x=5" est Int32, non Boolean.

Je suggère que si vous voyez cela dans le code de vos collègues, vous les éduquez aux voies des langues modernes et suggérez qu'ils écrivent la forme la plus naturelle à l'avenir.

Jon Skeet
la source
6
Mes collègues me rendent fou avec cela, et veulent des accolades même pour une seule déclaration après le if ... (au cas où vous ajouteriez quelque chose plus tard «sans vous en rendre compte»). Roulez sur F # et Boo (et YAML), si votre langue est illisible sans indentation, intégrez-la à la langue, dis-je.
Benjol
48
Ajouter des accolades est raisonnable, IMO - C # ne fait certainement aucune tentative pour empêcher que ce soit un problème. C'est très différent du problème "constante == variable".
Jon Skeet
6
a if (this! = that) {performAsSuch (); } est en fait assez sain. L'argument de "l'ajout ultérieur" me semble aussi fragile que de ne pas éviter / repérer si (a = 5) fautes de frappe
jpinto3912
3
@jpinto: Je ne suis pas vraiment sûr de ce que vous essayez de dire ici - que vous faites comme des accolades autour des déclarations simples, ou que vous ne faites pas? La différence entre cela et la variable business dans la question est qu'en C #, le code avec une faute de frappe est un code non valide , donc le compilateur l'arrêtera. La même chose n'est pas vraie pour ne pas mettre d'accolades.
Jon Skeet
3
@Benjol N'oubliez pas de toujours fournir une else { }clause vide au cas où quelqu'un aurait besoin d'y ajouter quelque chose plus tard et oublierait d'écrire le elsemot-clé lui-même. (Blague)
Jeppe Stig Nielsen
12

Il y a une bonne raison d'utiliser d'abord null: if(null == myDuck)

Si votre class Duckremplace l' ==opérateur, alors if(myDuck == null)peut entrer dans une boucle infinie.

Utiliser nullfirst utilise un comparateur d'égalité par défaut et fait réellement ce que vous vouliez.

(J'entends que vous vous habituez finalement à lire du code écrit de cette façon - je n'ai tout simplement pas encore vécu cette transformation)

Voici un exemple:

public class myDuck
{
    public int quacks;
    static override bool operator ==(myDuck a, myDuck b)
    {
        // these will overflow the stack - because the a==null reenters this function from the top again
        if (a == null && b == null)
            return true;
        if (a == null || b == null)
            return false;

        // these wont loop
        if (null == a && null == b)
            return true;
        if (null == a || null == b)
            return false;
        return a.quacks == b.quacks; // this goes to the integer comparison
    }
}
DanW
la source
11
C'est faux. Voté contre. Pointez simplement la souris sur l' ==opérateur et posez-le là, et vous verrez que la surcharge définie par l'utilisateur est utilisée dans les deux cas. Bien sûr, cela peut faire une différence subtile si vous vérifiez aavant b, puis permutez la position de l' aopérande droit à l'opérande gauche. Mais vous pouvez également vérifier bavant a, et ce b == nullserait alors celui qui a inversé l'ordre. Tout cela est déroutant pour que l'opérateur s'appelle lui-même. Au lieu de cela, transtypez l'opérande (ou les deux) en objectpour obtenir la surcharge souhaitée. Comme if ((object)a == null)ou if (a == (object)null).
Jeppe Stig Nielsen
10

Comme tout le monde l'a déjà noté, cela vient plus ou moins du langage C où vous pourriez obtenir un faux code si vous oubliez accidentellement le deuxième signe égal. Mais il y a une autre raison qui correspond également à C #: la lisibilité.

Prenons juste cet exemple simple:

if(someVariableThatShouldBeChecked != null
   && anotherOne != null
   && justAnotherCheckThatIsNeededForTestingNullity != null
   && allTheseChecksAreReallyBoring != null
   && thereSeemsToBeADesignFlawIfSoManyChecksAreNeeded != null)
{
    // ToDo: Everything is checked, do something...
}

Si vous permutez simplement tous les mots nuls au début, vous pouvez repérer beaucoup plus facilement toutes les vérifications:

if(null != someVariableThatShouldBeChecked
   && null != anotherOne
   && null != justAnotherCheckThatIsNeededForTestingNullity
   && null != allTheseChecksAreReallyBoring
   && null != thereSeemsToBeADesignFlawIfSoManyChecksAreNeeded)
{
    // ToDo: Everything is checked, do something...
}

Donc, cet exemple est peut-être un mauvais exemple (reportez-vous aux directives de codage), mais pensez simplement à faire défiler rapidement un fichier de code complet. En voyant simplement le motif

if(null ...

vous savez immédiatement ce qui va suivre.

Si c'était l'inverse, vous devez toujours scanner jusqu'à la fin de la ligne pour voir le contrôle de nullité, vous laissant simplement trébucher pendant une seconde pour savoir quel type de contrôle est effectué. Alors peut-être que la coloration syntaxique peut vous aider, mais vous êtes toujours plus lent lorsque ces mots-clés sont à la fin de la ligne au lieu du début.

Oliver
la source
9

Je suppose que c'est un programmeur C qui a changé de langue.

En C, vous pouvez écrire ce qui suit:

int i = 0;
if (i = 1)
{
    ...
}

Notez l'utilisation d'un seul signe égal ici, ce qui signifie que le code attribuera 1 à la variable i, puis retournera 1 (une affectation est une expression) et utilisera 1 dans l'instruction if, qui sera traitée comme vraie. En d'autres termes, ce qui précède est un bogue.

En C # cependant, ce n'est pas possible. Il n'y a en effet aucune différence entre les deux.

Lasse V. Karlsen
la source
1
"En C # cependant, ce n'est pas possible" sans que le compilateur ne se plaigne, car l'instruction if nécessite une expression booléenne, et celle fournie est de type int.
Robert Harvey le
4

Autrefois, les gens oubliaient le «!» (ou le supplément '=' pour l'égalité, qui est plus difficile à repérer) et faites une affectation au lieu d'une comparaison. mettre le null devant élimine la possibilité du bogue, puisque null n'est pas une valeur l (IE il ne peut pas être assigné).

La plupart des compilateurs modernes donnent un avertissement lorsque vous effectuez une affectation dans un conditionnel de nos jours, et C # donne en fait une erreur. La plupart des gens s'en tiennent au schéma var == null car il est plus facile à lire pour certaines personnes.

Rik
la source
Tous les compilateurs C # (notez que c'est une question C #) devraient ne pas compiler une instruction "if" où l'expression n'est pas un booléen cependant ...
Jon Skeet
4

Je ne vois aucun avantage à suivre cette convention. En C, où les types booléens n'existent pas, il est utile d'écrire

if( 5 == variable)

plutôt que

if (variable == 5)

car si vous oubliez l'un des signes égaux, vous vous retrouvez avec

if (variable = 5)

qui attribue 5 à la variable et toujours évalué à vrai. Mais en Java, un booléen est un booléen. Et avec! =, Il n'y a aucune raison du tout.

Un bon conseil, cependant, est d'écrire

if (CONSTANT.equals(myString))

plutôt que

if (myString.equals(CONSTANT))

car cela permet d'éviter les NullPointerExceptions.

Mon conseil serait de demander une justification de la règle. S'il n'y en a pas, pourquoi le suivre? Cela n'aide pas la lisibilité

Gokkula Soudan R
la source
0

Pour moi, c'est toujours le style que tu préfères

@Shy - Encore une fois, si vous confondez les opérateurs, vous devriez vouloir obtenir une erreur de compilation ou vous exécuterez du code avec un bogue - un bogue qui reviendra et vous mordra plus tard car il a produit un comportement inattendu

TheCodeJunkie
la source
Je ne pense pas avoir entendu parler de quelqu'un préférant la forme "constante == variable" autrement que pour des raisons de sécurité, qui sont déjà traitées en C #.
Jon Skeet
Je préfère moi-même "variable == constant", mais j'ai vu des programmeurs adopter l'inverse parce qu'ils trouvent que cela leur semble plus naturel.
TheCodeJunkie
1
Étaient-ils tous des gens avec des années de lavage de cerveau C derrière eux, ou était-ce un véritable choix?
Jon Skeet
0

Comme beaucoup l'ont souligné, c'est principalement dans l'ancien code C qu'il a été utilisé pour identifier une erreur de compilation, car le compilateur l'a accepté comme légal

Les nouveaux langages de programmation comme java, go sont suffisamment intelligents pour capturer de telles erreurs de compilation

Il ne faut pas utiliser "null! = Variable" comme des conditions dans le code car il est très illisible

Anand Shah
la source
-4

Une dernière chose ... Si vous comparez une variable à une constante (entier ou chaîne par exemple), mettre la constante à gauche est une bonne pratique car vous ne rencontrerez jamais NullPointerExceptions:

int i;
if(i==1){        // Exception raised: i is not initialized. (C/C++)
  doThis();
}

tandis que

int i;
if(1==i){        // OK, but the condition is not met.
  doThis();
}

Maintenant, puisque par défaut C # instancie toutes les variables, vous ne devriez pas avoir ce problème dans ce langage.

Karlipoppins
la source
1
Je ne sais pas quel compilateur vous utilisez pour le premier bit de code, mais il devrait compiler (généralement avec un avertissement). La valeur de i n'est pas définie, mais elle a une certaine valeur.
Dolphin
Bien sûr, les deux codes se compileront! C'est exactement le problème. Essayez d'exécuter le premier code et vous comprendrez ce que je voulais dire par "Exception soulevée" ...
karlipoppins
ne fait pas non plus la différence entre les deux. Pouvez-vous expliquer?
mfazekas
Aucune exception à moins que vous déclariez int * i en C / C ++, et même dans ce cas, il comparera les pointeurs et ne lèvera aucune exception ... Comme Dolphin l'a déclaré, la valeur n'est pas définie mais elle ne générera aucune exception.
Cobusve
aucune exception, disons que ic'est une valeur aléatoire sans une initialisation appropriée. l'expression a complètement la même sémantique en c / c ++
Raffaello