Warum sieht man in C # oft "null! = Variable" anstelle von "Variable! = Null"?

103

Gibt es in c # einen Unterschied in der Ausführungsgeschwindigkeit für die Reihenfolge, in der Sie die Bedingung angeben?

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

Seit kurzem habe ich das erste ziemlich oft gesehen, und es hat meine Aufmerksamkeit erregt, seit ich an das zweite gewöhnt war.

Wenn es keinen Unterschied gibt, was ist der Vorteil des ersten?

mr_georg
quelle
Einige Programmiersprachen unterstützen das Initialisieren von Werten unter if-Bedingungen. Wenn wir in solchen Sprachen LValue mit RValue vergleichen möchten, empfiehlt es sich, das Literal auf der linken Seite wie if (1 == i) zu verwenden. Wenn wir also versehentlich = eher == setzen, gibt es einen Compilerfehler.
Jugal Panchal

Antworten:

160

Es ist ein Überbleibsel von C. Wenn Sie in C entweder einen schlechten Compiler verwenden oder die Warnungen nicht hoch genug sind, wird dies ohne jegliche Warnung kompiliert (und ist in der Tat ein gesetzlicher Code):

// Probably wrong
if (x = 5)

als du eigentlich wohl gemeint hast

if (x == 5)

Sie können dies in C umgehen, indem Sie Folgendes tun:

if (5 == x)

Ein Tippfehler hier führt zu ungültigem Code.

Nun, in C # ist das alles piffle. Wenn Sie nicht zwei Boolesche Werte vergleichen (was selten vorkommt, IME), können Sie den besser lesbaren Code schreiben, da für eine "if" -Anweisung zunächst ein Boolescher Ausdruck erforderlich ist und der Typ " x=5" Int32nichtBoolean .

Ich schlage vor, wenn Sie dies im Code Ihrer Kollegen sehen, erziehen Sie sie in modernen Sprachen und schlagen vor, dass sie in Zukunft die natürlichere Form schreiben.

Jon Skeet
quelle
6
Meine Kollegen machen mich alle verrückt und wollen geschweifte Klammern auch für eine einzelne Aussage nach dem if ... (falls Sie später etwas hinzufügen, ohne es zu merken). Roll on F # und Boo (und YAML), wenn Ihre Sprache ohne Einrückung nicht lesbar ist, machen Sie sie Teil der Sprache, sage ich.
Benjol
48
Das Hinzufügen von Klammern ist vernünftig, IMO - C # unternimmt sicherlich keinen Versuch, das Problem zu verhindern. Das unterscheidet sich sehr vom Problem "Konstante == Variable".
Jon Skeet
6
a if (this! = that) {performAsSuch (); } ist eigentlich ganz gesund. Das Argument der "späteren Hinzufügung" scheint mir so fragil zu sein, als würde man nicht vermeiden / erkennen, wenn (a = 5) Tippfehler
jpinto3912
3
@jpinto: Ich bin nicht wirklich sicher , was Sie versuchen , hier zu sagen - , dass Sie tun , wie Klammern um einzelne Aussagen, oder dass Sie nicht? Der Unterschied zwischen diesem und dem variablen Geschäft in der Frage besteht darin, dass in C # der Code mit einem Tippfehler ungültiger Code ist, sodass der Compiler ihn stoppt. Das gleiche ist nicht wahr für nicht setzen Klammern in.
Jon Skeet
3
@Benjol Vergessen Sie nicht, immer eine leere else { }Klausel anzugeben, falls jemand dort später etwas hinzufügen muss und vergisst, das elseSchlüsselwort selbst zu schreiben . (Witz)
Jeppe Stig Nielsen
12

Es gibt einen guten Grund, zuerst null zu verwenden: if(null == myDuck)

Wenn Sie class Duckden ==Operator überschreiben , if(myDuck == null)können Sie in eine Endlosschleife gehen.

Bei der nullersten Verwendung wird ein Standard-Gleichstellungskomparator verwendet, der tatsächlich das tut, was Sie beabsichtigt haben.

(Ich habe gehört, dass Sie sich daran gewöhnt haben, Code zu lesen, der irgendwann so geschrieben wurde - ich habe diese Transformation noch nicht erlebt).

Hier ist ein Beispiel:

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
quelle
11
Das ist falsch. Abgestimmt. Zeigen Sie einfach mit der Maus über den ==Bediener und legen Sie ihn dort ab. In beiden Fällen wird die benutzerdefinierte Überlastung verwendet. Natürlich kann es einen subtilen Unterschied machen, wenn Sie avorher prüfen bund dann die Position des arechten Operanden zum linken Operanden vertauschen . Sie könnten aber auch bvorher nachsehen a, und dann b == nullwäre es derjenige, der die Reihenfolge umgekehrt hat. Dies ist alles verwirrend, wenn sich der Bediener selbst anruft. Wirken Sie stattdessen einen der Operanden (oder beide) object, um die gewünschte Überladung zu erhalten. Wie if ((object)a == null)oder if (a == (object)null).
Jeppe Stig Nielsen
10

Wie alle bereits bemerkt haben, kommt es mehr oder weniger aus der C-Sprache, wo Sie falschen Code erhalten könnten, wenn Sie versehentlich das zweite Gleichheitszeichen vergessen. Es gibt aber noch einen anderen Grund, der auch mit C # übereinstimmt: Lesbarkeit.

Nehmen Sie einfach dieses einfache Beispiel:

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

Wenn Sie einfach alle Nullwörter an den Anfang tauschen würden , könnten Sie alle Überprüfungen viel einfacher erkennen:

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

Dieses Beispiel ist vielleicht ein schlechtes Beispiel (siehe Codierungsrichtlinien), aber denken Sie nur daran, dass Sie schnell über eine vollständige Codedatei scrollen. Indem Sie einfach das Muster sehen

if(null ...

Sie wissen sofort, was als nächstes kommt.

Wenn es umgekehrt wäre, müssen Sie immer bis zum Ende der Zeile scannen , um die Nichtigkeitsprüfung zu sehen, und Sie müssen nur eine Sekunde lang stolpern, um herauszufinden, welche Art von Prüfung dort durchgeführt wird. Vielleicht hilft Ihnen die Hervorhebung der Syntax, aber Sie sind immer langsamer, wenn sich diese Schlüsselwörter am Ende der Zeile anstatt am Anfang befinden.

Oliver
quelle
9

Ich denke, dies ist ein C-Programmierer, der die Sprache gewechselt hat.

In C können Sie Folgendes schreiben:

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

Beachten Sie die Verwendung eines einzelnen Gleichheitszeichens, was bedeutet, dass der Code der Variablen i 1 zuweist, dann 1 zurückgibt (eine Zuweisung ist ein Ausdruck) und 1 in der if-Anweisung verwendet, die als wahr behandelt wird. Mit anderen Worten, das Obige ist ein Fehler.

In C # ist dies jedoch nicht möglich. Es gibt in der Tat keinen Unterschied zwischen den beiden.

Lasse V. Karlsen
quelle
1
"In C # ist dies jedoch nicht möglich", ohne dass sich der Compiler beschwert, da die if-Anweisung einen booleschen Ausdruck erfordert und der angegebene vom Typ int ist.
Robert Harvey
4

In früheren Zeiten vergaßen die Leute das '!' (oder das zusätzliche '=' für Gleichheit, das schwieriger zu erkennen ist) und führen Sie eine Zuweisung anstelle eines Vergleichs durch. Wenn Sie die Null voranstellen, wird die Möglichkeit für den Fehler ausgeschlossen, da null kein l-Wert ist (IE kann ihm nicht zugewiesen werden).

Die meisten modernen Compiler geben heutzutage eine Warnung aus, wenn Sie eine Zuweisung unter bestimmten Bedingungen ausführen, und C # gibt tatsächlich einen Fehler aus. Die meisten Leute halten sich einfach an das Schema var == null, da es für manche Leute einfacher zu lesen ist.

Rik
quelle
Alle C # -Compiler (beachten Sie, dass dies eine C # -Frage ist) sollten keine "if" -Anweisung kompilieren, bei der der Ausdruck jedoch kein Boolescher Wert ist ...
Jon Skeet
4

Ich sehe keinen Vorteil darin, dieser Konvention zu folgen. In C, wo es keine booleschen Typen gibt, ist es nützlich zu schreiben

if( 5 == variable)

eher, als

if (variable == 5)

denn wenn Sie eines der Gleichheitszeichen vergessen, erhalten Sie am Ende

if (variable = 5)

Dies weist der Variablen 5 zu und wertet immer true aus. In Java ist ein Boolescher Wert ein Boolescher Wert. Und mit! = Gibt es überhaupt keinen Grund.

Ein guter Rat ist jedoch zu schreiben

if (CONSTANT.equals(myString))

eher, als

if (myString.equals(CONSTANT))

weil es hilft, NullPointerExceptions zu vermeiden.

Mein Rat wäre, um eine Begründung der Regel zu bitten. Wenn es keine gibt, warum dann folgen? Es hilft nicht die Lesbarkeit

Gokkula Sudan R.
quelle
0

Für mich war es immer der Stil, den Sie bevorzugen

@Shy - Wenn Sie die Operatoren verwirren, sollten Sie einen Kompilierungsfehler erhalten, oder Sie führen Code mit einem Fehler aus - einem Fehler, der zurückkommt und Sie später beißt, da er unerwartetes Verhalten hervorruft

TheCodeJunkie
quelle
Ich glaube nicht , dass ich jemals jemand bevorzugt die „Konstante == Variable“ Form gehört habe andere als aus Gründen der Sicherheit, die mit in C # bereits behandelt werden.
Jon Skeet
Ich selbst bevorzuge "variable == Konstante", aber ich habe gesehen, dass Programmierer das Umgekehrte übernommen haben, weil sie der Meinung sind, dass es für sie natürlicher ist.
TheCodeJunkie
1
Waren sie alle Menschen mit jahrelanger C-Gehirnwäsche oder war es eine echte Wahl?
Jon Skeet
0

Wie viele betonten, wurde es hauptsächlich in altem C-Code verwendet, um Kompilierungsfehler zu identifizieren, da der Compiler dies als legal akzeptierte

Neue Programmiersprachen wie Java, go sind intelligent genug, um solche Kompilierungsfehler zu erfassen

Man sollte keine "null! = Variablen" -ähnlichen Bedingungen im Code verwenden, da diese sehr unlesbar sind

Anand Shah
quelle
-4

Noch etwas ... Wenn Sie eine Variable mit einer Konstanten vergleichen (z. B. Ganzzahl oder Zeichenfolge), empfiehlt es sich, die Konstante links zu setzen, da Sie nie auf NullPointerExceptions stoßen:

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

wohingegen

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

Da C # standardmäßig alle Variablen instanziiert, sollte dieses Problem in dieser Sprache nicht auftreten.

Karlipoppins
quelle
1
Ich bin nicht sicher, welchen Compiler Sie für das erste Codebit verwenden, aber er sollte kompiliert werden (im Allgemeinen mit einer Warnung). Der Wert von i ist undefiniert, hat aber EINIGEN Wert.
Dolphin
Natürlich werden beide Codes kompiliert! Das ist genau das Problem.
Wenn
Ist auch nicht der Unterschied zwischen den beiden. Können Sie arbeiten?
Mfazekas
Keine Ausnahme, es sei denn, Sie deklarieren int * i in C / C ++, und selbst in diesem Fall werden Zeiger verglichen und keine Ausnahme ausgelöst ... Wie von Dolphin angegeben, ist der Wert undefiniert, es werden jedoch keine Ausnahmen generiert.
Cobusve
Keine Ausnahmen, sagen wir, es ihandelt sich um einen zufälligen Wert ohne ordnungsgemäße Initialisierung. Der Ausdruck hat völlig die gleiche Semantik in c / c ++
Raffaello