Nachdem ich mein Projekt von VS2013 auf VS2015 migriert habe, wird das Projekt nicht mehr erstellt. In der folgenden LINQ-Anweisung tritt ein Kompilierungsfehler auf:
static void Main(string[] args)
{
decimal a, b;
IEnumerable<dynamic> array = new string[] { "10", "20", "30" };
var result = (from v in array
where decimal.TryParse(v, out a) && decimal.TryParse("15", out b) && a <= b // Error here
orderby decimal.Parse(v)
select v).ToArray();
}
Der Compiler gibt einen Fehler zurück:
Fehler CS0165 Verwendung der nicht zugewiesenen lokalen Variablen 'b'
Was verursacht dieses Problem? Ist es möglich, es über eine Compilereinstellung zu beheben?
b
nachdem es über einenout
Parameter zugewiesen wurde.out
Argumenten schlecht ist . Würde das einenTryParse
nullbaren Wert (oder einen äquivalenten Wert) zurückgeben?where (a = decimal.TryParse(v)).HasValue && (b = decimal.TryParse(v)).HasValue && a <= b
sieht viel besser ausdecimal a, b; var q = decimal.TryParse((dynamic)"10", out a) && decimal.TryParse("15", out b) && a <= b;
. Ich habe einen Roslyn-Fehler geöffnet , der dies auslöst.Antworten:
Sieht für mich wie ein Compiler-Bug aus. Zumindest war es so. Obwohl die Ausdrücke
decimal.TryParse(v, out a)
unddecimal.TryParse(v, out b)
dynamisch ausgewertet werden, habe ich erwartet , dass der Compiler immer noch versteht, dassa <= b
beidea
undb
definitiv zugewiesen sind, wenn sie erreicht sind. Selbst mit den Verrücktheiten, die beim dynamischen Tippen auftreten können, würde ich erwarten, dass diea <= b
Bewertung erst nach der Bewertung beiderTryParse
Anrufe erfolgt.Es stellt sich jedoch heraus, dass es durch Operator und Konvertierung schwierig ist, einen Ausdruck zu haben,
A && B && C
der ausgewertet wirdA
undC
aber nichtB
- wenn Sie schlau genug sind. Das geniale Beispiel von Neal Gafter finden Sie im Roslyn-Fehlerbericht .Es
dynamic
ist noch schwieriger , diese Arbeit zu erledigen - die Semantik bei dynamischen Operanden ist schwieriger zu beschreiben, da Sie zur Durchführung der Überlastungsauflösung Operanden auswerten müssen, um herauszufinden, um welche Typen es sich handelt, was möglicherweise nicht intuitiv ist. Aber auch hier ist Neal hat mit einem Beispiel , das zeigt , dass die Compiler - Fehler kam erforderlich ... dies ist kein Fehler, es ist ein Bug - Fix . Riesige Anerkennung an Neal für den Beweis.Nein, aber es gibt Alternativen, die den Fehler vermeiden.
Erstens könnten Sie verhindern, dass es dynamisch ist. Wenn Sie wissen, dass Sie immer nur Zeichenfolgen verwenden, können Sie der Bereichsvariablen den Typ (dh ) verwenden
IEnumerable<string>
oder geben . Das wäre meine bevorzugte Option.v
string
from string v in array
Wenn Sie es wirklich dynamisch halten müssen, geben Sie zunächst
b
einen Wert an:Dies wird nicht schaden - wir wissen , dass tatsächlich Ihre dynamische Auswertung etwas verrückt nicht tun, so dass Sie immer noch einen Wert zuweisen am Ende dann ,
b
bevor Sie es verwenden, so dass der Ausgangswert irrelevant.Darüber hinaus scheint das Hinzufügen von Klammern auch zu funktionieren:
Dies ändert den Punkt, an dem verschiedene Teile der Überlastungsauflösung ausgelöst werden, und macht den Compiler glücklich.
Es bleibt noch ein Problem offen: Die Regeln der Spezifikation für die definitive Zuordnung zum
&&
Operator müssen präzisiert werden, um festzustellen, dass sie nur gelten, wenn der&&
Operator in seiner "regulären" Implementierung mit zweibool
Operanden verwendet wird. Ich werde versuchen sicherzustellen, dass dies für den nächsten ECMA-Standard behoben ist.quelle
IEnumerable<string>
oder Hinzufügen von Klammern hat bei mir funktioniert. Jetzt erstellt der Compiler fehlerfrei.decimal a, b = 0m;
könnte den Fehler beseitigen,a <= b
würde dann aber immer verwenden0m
, da der out-Wert noch nicht berechnet wurde.decimal? TryParseDecimal(string txt)
kann auch eine Lösung seinb
möglicherweise nicht zugewiesen wird"; Ich weiß, dass das eine ungültige Argumentation ist, aber es erklärt, warum die Klammern esDies scheint ein Fehler oder zumindest eine Regression im Roslyn-Compiler zu sein. Der folgende Fehler wurde behoben, um ihn zu verfolgen:
In der Zwischenzeit hat Jons ausgezeichnete Antwort ein paar Probleme.
quelle
Da ich im Fehlerbericht so hart geschult wurde, werde ich versuchen, dies selbst zu erklären.
Stellen Sie sich vor, es
T
handelt sich um einen benutzerdefinierten Typ mit einer impliziten Umwandlung,bool
die zwischenfalse
und wechselttrue
, beginnend mitfalse
. Soweit der Compiler weiß, könnte dasdynamic
erste Argument zum ersten&&
für diesen Typ ausgewertet werden, daher muss es pessimistisch sein.Wenn dann der Code kompiliert wird, kann dies passieren:
&&
, führt er Folgendes aus:T
- implizit gegossenbool
.false
, also müssen wir das zweite Argument nicht bewerten.&&
Auswertung als erstes Argument. (Nein,false
aus irgendeinem Grund nicht.)&&
, führt er Folgendes aus:T
- implizit gegossenbool
.true
, also bewerte das zweite Argument.b
ist nicht zugeordnet.Kurz gesagt, es gibt spezielle "definitive Zuweisungs" -Regeln, die uns nicht nur sagen lassen, ob eine Variable "definitiv zugewiesen" oder "nicht definitiv zugewiesen" ist, sondern auch, ob sie "definitiv nach
false
Anweisung zugewiesen" oder "definitiv zugewiesen" ist zugewiesen nachtrue
Anweisung ".Diese sind vorhanden, damit der Compiler beim Umgang mit
&&
und||
(und!
und??
und?:
) prüfen kann, ob Variablen in bestimmten Zweigen eines komplexen booleschen Ausdrucks zugewiesen werden können.Diese funktionieren jedoch nur, solange die Typen der Ausdrücke boolesch bleiben . Wenn ein Teil des Ausdrucks
dynamic
(oder ein nicht-boolescher statischer Typ) ist, können wir nicht mehr zuverlässig sagen, dass der Ausdrucktrue
oderfalse
- wenn wir ihn das nächste Mal umwandeln, umbool
zu entscheiden, welcher Zweig verwendet werden soll, hat er möglicherweise seine Meinung geändert.Update: Dies wurde nun behoben und dokumentiert :
quelle
out
zu einer Methode mit verfügtref
. Es wird es gerne tun und es werden Variablen zugewiesen, ohne den Wert zu ändern.false
/true
im Gegensatz zum impliziten Cast-Operator zu verwenden? Lokal wirdimplicit operator bool
das erste Argument aufgerufen , dann der zweite Operand aufgerufen,operator false
der erste Operand aufgerufen , gefolgt vomimplicit operator bool
ersten Operanden erneut . Das macht für mich keinen Sinn, der erste Operand sollte sich im Wesentlichen einmal auf einen Booleschen Wert beschränken, oder?dynamic
verkettete&&
Fall? Ich habe gesehen, wie es im Grunde ging (1) das erste Argument auswerten (2) implizite Besetzung verwenden, um zu sehen, ob ich kurzschließen kann (3) ich kann nicht, also evauiere das zweite Argument (4) jetzt kenne ich beide Typen, ich Das Beste&&
ist ein benutzerdefinierter&
(5) Aufrufoperatorfalse
für das erste Argument, um zu sehen, ob ich kurzschließen kann. (6) Ich kann (weilfalse
und nichtimplicit bool
einverstanden). Das Ergebnis ist also das erste Argument ... und dann das nächste&&
, (7) benutze implizite Besetzung, um zu sehen, ob ich (wieder) kurzschließen kann.Dies ist kein Fehler. Unter https://github.com/dotnet/roslyn/issues/4509#issuecomment-130872713 finden Sie ein Beispiel dafür, wie ein dynamischer Ausdruck dieses Formulars eine solche ausgelassene Variable nicht zuweisen kann.
quelle