Nicht-Kurzschlüsse boolescher Operatoren und C # 7 Mustervergleich

8

Ich schreibe gerade eine C # -Anwendung für .NET 4.7 (C # 7). Ich bin verwirrt, nachdem ich versucht habe, die neue Art der Deklaration einer Variablen mit dem Schlüsselwort "is" zu verwenden:     %Code% So funktioniert es, aber wenn es geht:

%Vor%

Visual Studio (Ich benutze 2017) zeigt mir den Fehler if (variable is MyClass classInstance) . Mit der Kurzversion von Use of unassigned local variable 'classInstance' ( & ) funktioniert es einwandfrei. Fehle ich etwas an dem Operator && ? (Ich weiß, dass die Verwendung der Kurzversionen viel häufiger verwendet wird, aber an dieser Stelle bin ich nur neugierig)

    
BMI24 24.08.2017, 16:27
quelle

2 Antworten

7

Dieser hat meinen Kopf verletzt, aber ich denke, ich habe es herausgefunden.

Diese Verwirrung wird durch zwei Macken verursacht: die Art und Weise, wie die is -Operation die Variable nicht deklariert (nicht null) belässt und wie der Compiler boolesche, aber nicht bitweise Operationen entfernt.

Quirk 1. Wenn die Umwandlung fehlschlägt, ist die Variable nicht zugewiesen (nicht null)

Nach der Dokumentation für die neue is -Syntax :

  

Wenn exp wahr ist und is mit einer if-Anweisung verwendet wird, wird varname zugewiesen und hat nur innerhalb der if-Anweisung einen lokalen Gültigkeitsbereich.

Wenn Sie zwischen den Zeilen lesen, bedeutet dies, dass die Variable als nicht zugewiesen betrachtet wird, wenn die Umwandlung is fehlschlägt. Dies kann kontraintuitiv sein (einige könnten erwarten, dass es stattdessen null ist). Das bedeutet, dass jeder Code im Block if , der auf der Variablen basiert, nicht kompiliert wird, wenn eine -Möglichkeit besteht, dass die gesamte if -Klausel auf true auswerten könnte. ohne eine Art Übereinstimmung vorhanden. So zum Beispiel

Dies kompiliert:

%Vor%

Und das kompiliert:

%Vor%

Aber das ist nicht:

%Vor%

Quirk 2. Boolesche Operationen sind wegoptimiert, binäre sind nicht

Wenn der Compiler ein vorbestimmtes boolesches Ergebnis erkennt, gibt der Compiler keinen unerreichbaren Code aus und überspringt bestimmte Validierungen. Zum Beispiel:

Dies kompiliert:

%Vor%

Aber wenn Sie & anstelle von && verwenden, kompiliert es nicht:

%Vor%

Wenn der Compiler etwas wie true && sieht, wird es einfach ignoriert. Also

%Vor%

Ist genau das Gleiche wie

%Vor%

Aber das

%Vor%

Ist nicht das Gleiche. Der Compiler muss weiterhin Code ausgeben, der die Operation & ausführt und seine Ausgabe in einer bedingten Anweisung verwendet. Oder selbst wenn dies nicht der Fall ist, führt der aktuelle C # 7-Compiler offenbar die gleichen Validierungen durch, als ob er es wäre. Dies mag ein wenig seltsam erscheinen, aber bedenken Sie, dass wenn Sie & anstelle von && verwenden, es eine Garantie gibt, dass & ausgeführt werden muss, und obwohl es in diesem Beispiel unwichtig scheint, muss der allgemeine Fall dies zulassen für zusätzliche komplexierende Faktoren, wie zum Beispiel das Überladen von Operatoren.

Wie die Macken zusammenkommen

Im letzten Beispiel wird das Ergebnis der if -Klausel zur Laufzeit bestimmt, nicht die Kompilierzeit. Daher kann der Compiler nicht sicher sein, dass y am Ende zugewiesen wird, bevor der Inhalt des Blocks if ausgeführt wird. So bekommst du

%Vor%

TLDR

In der Situation einer zusammengesetzten logischen Operation kann c # nicht sicher sein, dass die gesamte if -Bedingung nur dann zu true wird, wenn der Cast erfolgreich ist. Ohne diese Gewissheit kann der Zugriff auf die Variable nicht zugelassen werden, da sie möglicherweise nicht zugewiesen ist. Eine Ausnahme wird gemacht, wenn der Ausdruck zum Kompilierungszeitpunkt auf eine nicht-zusammengesetzte Operation reduziert werden kann, z. B. durch Entfernen von true && .

Workaround

Ich denke, die Art, wie wir die neue is -Syntax verwenden sollen, ist eine einzige Bedingung mit einer if -Klausel. Das Hinzufügen von true && am Anfang funktioniert, weil der Compiler es einfach entfernt. Aber alles andere, kombiniert mit der neuen Syntax, erzeugt Unklarheit darüber, ob die neue Variable in einem nicht zugewiesenen Zustand sein wird, wenn der Codeblock ausgeführt wird, und der Compiler kann dies nicht zulassen.

Die Problemumgehung besteht natürlich darin, Ihre Bedingungen zu verschachteln, anstatt sie zu kombinieren:

Funktioniert nicht:

%Vor%

Funktioniert gut:

%Vor%     
John Wu 24.08.2017 18:54
quelle
1
  

Vermisse ich etwas über die & amp; Betreiber?

Nein, das glaube ich nicht. Ihre Erwartungen scheinen mir richtig. Betrachten Sie dieses alternative Beispiel, das ohne Fehler kompiliert:

%Vor%

(Für mich ist es interessanter, die Zuweisung außerhalb des if -Körpers zu betrachten, da das Kurzschlussverhalten das Verhalten beeinflussen würde.)

Bei der expliziten Zuweisung erkennt der Compiler classInstance als eindeutig zugewiesen, in den Zuweisungen von a und b . Es sollte das Gleiche mit der neuen Syntax tun können.

Bei logischem and sollte ein Kurzschluss oder nicht wichtig sein. Ihr erster Wert ist true , daher sollte die zweite Hälfte immer ausgewertet werden, um den gesamten Ausdruck zu erhalten. Wie Sie festgestellt haben, behandelt der Compiler die & und && jedoch anders, was unerwartet ist.

Eine Variation dazu ist dieser Code:

%Vor%

Der Compiler identifiziert classInstance als nicht eindeutig zugewiesen, wenn || verwendet wird, hat aber das gleiche offensichtliche Fehlverhalten mit | (dh auch, dass classInstance nicht eindeutig zugewiesen ist), obwohl mit dem non Kurz-Operator, die Zuweisung muss unabhängig geschehen.

Auch hier funktioniert die obige Anweisung korrekt und die neue Syntax wird verwendet.

Wenn dies nur wäre, wenn die definitiven Zuweisungsregeln nicht mit der neuen Syntax behandelt würden, dann würde ich erwarten, dass && genauso fehlerhaft ist wie & . Aber es ist nicht. Der Compiler handhabt das korrekt. Und zwar in der Feature-Dokumentation (ich zögere, "Spezifikation" zu sagen, Da es noch keine ECMA-ratifizierte C # 7-Spezifikation gibt) lautet es:

  

Das type_pattern testet beide, dass ein Ausdruck einen bestimmten Typ hat, und wandelt ihn bei erfolgreichem Test in diesen Typ um. Dies führt eine lokale Variable des angegebenen Typs ein, die durch den angegebenen Bezeichner benannt wird. Diese lokale Variable wird definitiv zugewiesen, wenn das Ergebnis der Mustererkennung stimmt. [Betonung meiner]

Da Kurzschlüsse korrektes Verhalten ohne Musterabgleich erzeugen und Mustervergleiche ein korrektes Verhalten ohne Kurzschlüsse erzeugen (und eine eindeutige Zuweisung wird explizit in der Merkmalbeschreibung angesprochen), würde ich sagen, dass dies ein Compiler-Fehler ist . Es gibt wahrscheinlich eine übersehene Interaktion zwischen nicht-kurzschließenden booleschen Operatoren und die Art und Weise, wie der musterangepasste Ausdruck ausgewertet wird, der dazu führt, dass die bestimmte Zuweisung im Shuffle verloren geht.

Sie sollten darüber nachdenken, dies den Behörden mitzuteilen. Ich denke, in diesen Tagen ist das Roslyn GitHub Issue-Tracking , wo sie diese Art von Dingen verfolgen. Es könnte hilfreich sein, wenn Sie in Ihrem Bericht erläutern, wie Sie diese gefunden haben und warum diese spezielle Syntax in Ihrem Szenario wichtig ist (da der Operator && in dem von Ihnen geposteten Code gleichwertig funktioniert ... das nicht-kurzschließende & doesn ' t scheint dem Code einen Vorteil zu verschaffen.)

    
Peter Duniho 24.08.2017 18:50
quelle

Tags und Links