Catatan: ini tampaknya telah diperbaiki di Roslyn
Pertanyaan ini muncul ketika menulis jawaban saya untuk yang ini , yang berbicara tentang asosiatifitas operator penggabungan nol .
Sama seperti pengingat, ide dari operator null-coalescing adalah ekspresi dari form
x ?? y
pertama mengevaluasi x
, lalu:
- Jika nilai
x
nol,y
dievaluasi dan itu adalah hasil akhir dari ekspresi - Jika nilai
x
non-null,y
yang tidak dievaluasi, dan nilaix
adalah hasil akhir dari ekspresi, setelah konversi ke jenis kompilasi-waktuy
jika diperlukan
Sekarang biasanya tidak perlu konversi, atau itu hanya dari jenis nullable ke non-nullable - biasanya jenisnya sama, atau hanya dari (katakanlah) int?
ke int
. Namun, Anda dapat membuat operator konversi tersirat Anda sendiri, dan itu digunakan jika perlu.
Untuk kasus sederhana x ?? y
, saya belum melihat perilaku aneh. Namun, dengan (x ?? y) ?? z
saya melihat beberapa perilaku membingungkan.
Berikut ini adalah program pengujian singkat tapi lengkap - hasilnya ada di komentar:
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;
}
}
Jadi kami memiliki tiga tipe nilai khusus A
,, B
dan C
, dengan konversi dari A ke B, A ke C, dan B ke C.
Saya dapat memahami kasus kedua dan ketiga ... tetapi mengapa ada konversi tambahan A ke B dalam kasus pertama? Khususnya, saya benar - benar berharap kasus pertama dan kedua menjadi hal yang sama - setelah itu, hanya mengekstraksi ekspresi menjadi variabel lokal.
Adakah yang tahu apa yang terjadi? Saya sangat ragu untuk menangis "bug" ketika datang ke kompiler C #, tapi saya bingung apa yang terjadi ...
EDIT: Oke, ini contoh buruk tentang apa yang terjadi, berkat jawaban konfigurator, yang memberi saya alasan lebih lanjut untuk menganggapnya sebagai bug. Sunting: Sampel bahkan tidak perlu dua operator penggabungan nol sekarang ...
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;
}
}
Output dari ini adalah:
Foo() called
Foo() called
A to int
Fakta bahwa Foo()
dipanggil dua kali di sini sangat mengejutkan bagi saya - saya tidak dapat melihat alasan mengapa ekspresi dievaluasi dua kali.
C? first = ((B?)(((B?)x) ?? ((B?)y))) ?? ((C?)z);
. Anda akan mendapatkan:Internal Compiler Error: likely culprit is 'CODEGEN'
(("working value" ?? "user default") ?? "system default")