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
xnull ist,ywird ausgewertet und das ist das Endergebnis des Ausdrucks - Wenn der Wert von
xnicht null ist,ywird er nicht ausgewertet, und der Wert vonxist das Endergebnis des Ausdrucks nach einer Konvertierung in den Kompilierungszeittyp von,yfalls 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) ?? zmir 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 Cmit 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")