Hinweis: Dies scheint behoben worden zu sein Roslyn
Diese Frage stellte sich, als ich meine Antwort auf diese Frage schrieb , in der es um die Assoziativität der Null-Koaleszenz-Operators geht .
Nur zur Erinnerung, die Idee des Null-Koaleszenz-Operators ist, dass ein Ausdruck des Formulars
x ?? y
zuerst bewertet x
, dann:
- Wenn der Wert von
x
null ist,y
wird ausgewertet und das ist das Endergebnis des Ausdrucks - Wenn der Wert von
x
nicht null ist,y
wird er nicht ausgewertet, und der Wert vonx
ist das Endergebnis des Ausdrucks nach einer Konvertierung in den Kompilierungszeittyp von,y
falls erforderlich
Normalerweise ist jetzt keine Konvertierung mehr erforderlich, oder es wird nur von einem nullbaren Typ in einen nicht nullbaren Typ konvertiert - normalerweise sind die Typen gleich oder nur von (sagen wir) int?
nachint
. Sie können jedoch Ihre eigenen impliziten Konvertierungsoperatoren erstellen, die bei Bedarf verwendet werden.
Für den einfachen Fall von x ?? y
ich kein merkwürdiges Verhalten gesehen. Doch mit (x ?? y) ?? z
mir etwas verwirrend Verhalten sehen.
Hier ist ein kurzes, aber vollständiges Testprogramm - die Ergebnisse finden Sie in den Kommentaren:
using System;
public struct A
{
public static implicit operator B(A input)
{
Console.WriteLine("A to B");
return new B();
}
public static implicit operator C(A input)
{
Console.WriteLine("A to C");
return new C();
}
}
public struct B
{
public static implicit operator C(B input)
{
Console.WriteLine("B to C");
return new C();
}
}
public struct C {}
class Test
{
static void Main()
{
A? x = new A();
B? y = new B();
C? z = new C();
C zNotNull = new C();
Console.WriteLine("First case");
// This prints
// A to B
// A to B
// B to C
C? first = (x ?? y) ?? z;
Console.WriteLine("Second case");
// This prints
// A to B
// B to C
var tmp = x ?? y;
C? second = tmp ?? z;
Console.WriteLine("Third case");
// This prints
// A to B
// B to C
C? third = (x ?? y) ?? zNotNull;
}
}
Wir haben also drei benutzerdefinierte Werttypen A
.B
und C
mit Umwandlungen von A nach B, A bis C und B bis C.
Ich kann sowohl den zweiten als auch den dritten Fall verstehen ... aber warum gibt es im ersten Fall eine zusätzliche Umwandlung von A nach B. Insbesondere würde ich wirklich erwartet, dass der erste und der zweite Fall dasselbe sind - es wird schließlich nur ein Ausdruck in eine lokale Variable extrahiert.
Irgendwelche Abnehmer, was los ist? Ich zögere sehr, "Bug" zu schreien, wenn es um den C # -Compiler geht, aber ich bin ratlos, was los ist ...
EDIT: Okay, hier ist ein schlimmeres Beispiel dafür, was los ist, dank der Antwort des Konfigurators, die mir weiteren Grund gibt zu glauben, dass es ein Fehler ist. BEARBEITEN: Das Beispiel benötigt jetzt nicht einmal zwei Null-Koaleszenz-Operatoren ...
using System;
public struct A
{
public static implicit operator int(A input)
{
Console.WriteLine("A to int");
return 10;
}
}
class Test
{
static A? Foo()
{
Console.WriteLine("Foo() called");
return new A();
}
static void Main()
{
int? y = 10;
int? result = Foo() ?? y;
}
}
Die Ausgabe davon ist:
Foo() called
Foo() called
A to int
Die Tatsache, dass Foo()
hier zweimal aufgerufen wird, ist für mich sehr überraschend - ich sehe keinen Grund, den Ausdruck zweimal zu bewerten .
C? first = ((B?)(((B?)x) ?? ((B?)y))) ?? ((C?)z);
. Sie erhalten:Internal Compiler Error: likely culprit is 'CODEGEN'
(("working value" ?? "user default") ?? "system default")