Nota: questo sembra essere stato risolto a Roslyn
Questa domanda è nata quando ho scritto la mia risposta a questa , che parla dell'associatività dell'operatore a coalescenza nulla .
Proprio come promemoria, l'idea dell'operatore a coalescenza nulla è che un'espressione della forma
x ?? y
prima valuta x
, quindi:
- Se il valore di
x
è null,y
viene valutato e questo è il risultato finale dell'espressione - Se il valore di
x
non è null, nony
viene valutato e il valore di è il risultato finale dell'espressione, dopo una conversione al tipo di tempo di compilazione di se necessariox
y
Ora di solito non c'è bisogno di una conversione, o è solo da un tipo nullable a uno non nullable - di solito i tipi sono gli stessi, o semplicemente da (diciamo) int?
a int
. Tuttavia, puoi creare i tuoi operatori di conversione impliciti e questi vengono utilizzati dove necessario.
Per il semplice caso di x ?? y
, non ho visto alcun comportamento strano. Tuttavia, (x ?? y) ?? z
vedo alcuni comportamenti confusi.
Ecco un programma di test breve ma completo: i risultati sono nei commenti:
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;
}
}
Quindi abbiamo tre tipi di valore personalizzati, A
, B
e C
, con le conversioni da A a B, da A a C, e B a C.
Riesco a capire sia il secondo che il terzo caso ... ma perché c'è una conversione da A a B in più nel primo caso? In particolare, mi sarei aspettato davvero che il primo caso e il secondo caso fossero la stessa cosa - dopo tutto, è solo estrarre un'espressione in una variabile locale.
Qualche appassionato di quello che sta succedendo? Sono estremamente riluttante a piangere "bug" quando si tratta del compilatore C #, ma sono sconcertato da quello che sta succedendo ...
EDIT: Okay, ecco un esempio più cattivo di quello che sta succedendo, grazie alla risposta del configuratore, che mi dà ulteriori motivi per pensare che sia un bug. EDIT: il campione non ha nemmeno bisogno di due operatori a coalescenza nulla ora ...
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;
}
}
L'output di questo è:
Foo() called
Foo() called
A to int
Il fatto che Foo()
venga chiamato due volte qui è estremamente sorprendente per me: non vedo alcun motivo per cui l'espressione venga valutata due volte.
C? first = ((B?)(((B?)x) ?? ((B?)y))) ?? ((C?)z);
. Otterrai:Internal Compiler Error: likely culprit is 'CODEGEN'
(("working value" ?? "user default") ?? "system default")