Uwaga: wydaje się, że zostało to naprawione w Roslyn
To pytanie powstało, pisząc moją odpowiedź na to , które mówi o asocjatywności operatora zerowego koalescencji .
Przypominamy, że operatorem zerowego koalescencji jest wyrażenie formy
x ?? y
najpierw ocenia x
, a następnie:
- Jeśli wartość
x
jest równa null,y
jest obliczana i jest to końcowy wynik wyrażenia - Jeśli wartość
x
jest różna od null, niey
jest obliczana, a wartość jest końcowym wynikiem wyrażenia po konwersji na typ czasu kompilacji w razie potrzebyx
y
Teraz zwykle nie ma potrzeby konwersji, lub jest to po prostu z typu zerowalnego na nieuznawalny - zwykle typy są takie same lub po prostu (powiedzmy) int?
do int
. Jednakże, można tworzyć własne operatory konwersji niejawnych, a te są stosowane tam, gdzie jest to konieczne.
W prostym przypadku x ?? y
nie widziałem żadnego dziwnego zachowania. Jednak (x ?? y) ?? z
widzę pewne mylące zachowanie.
Oto krótki, ale kompletny program testowy - wyniki znajdują się w komentarzach:
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;
}
}
Więc mamy trzy typy wartości zwyczaj, A
, B
i C
, z konwersji z punktu A do B, A do C, a B do C.
Rozumiem zarówno drugi przypadek, jak i trzeci przypadek ... ale dlaczego w pierwszym przypadku występuje dodatkowa konwersja A do B. W szczególności, bym naprawdę nie spodziewał się, że pierwszy przypadek i drugi przypadek będzie to samo - to tylko wydobywania wyrażenia do zmiennej lokalnej, po wszystkim.
Masz ochotę na to, co się dzieje? Jestem wyjątkowo niezdecydowany, by wykrzykiwać „błąd”, jeśli chodzi o kompilator C #, ale jestem zaskoczony tym, co się dzieje ...
EDYCJA: OK, oto nieprzyjemny przykład tego, co się dzieje, dzięki odpowiedzi konfiguratora, która daje mi kolejny powód, by myśleć, że to błąd. EDYCJA: Próbka nie potrzebuje teraz nawet dwóch zerowych operatorów koalescencyjnych ...
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;
}
}
Wynikiem tego jest:
Foo() called
Foo() called
A to int
Fakt, że Foo()
tutaj wywoływane są dwa razy, jest dla mnie ogromnie zaskakujący - nie widzę żadnego powodu, aby wyrażenie było oceniane dwukrotnie.
C? first = ((B?)(((B?)x) ?? ((B?)y))) ?? ((C?)z);
. Otrzymasz:Internal Compiler Error: likely culprit is 'CODEGEN'
(("working value" ?? "user default") ?? "system default")