Apa yang membuat ValueTuple kovarian?

35

Ini mengkompilasi dengan benar dalam C # 7.3 (Kerangka 4.8):

(string, string) s = ("a", "b");
(object, string) o = s;

Saya tahu ini adalah gula sintaksis untuk yang berikut, yang juga mengkompilasi dengan benar:

ValueTuple<string, string> s = new ValueTuple<string, string>("a", "b");
ValueTuple<object, string> o = s;

Jadi, tampaknya ValueTuples dapat ditetapkan secara kovarian , yang luar biasa !

Sayangnya, saya tidak mengerti mengapa : Saya mendapat kesan bahwa C # hanya mendukung kovarians pada antarmuka dan delegasi . ValueTypebukan keduanya.

Bahkan, ketika saya mencoba menduplikasi fitur ini dengan kode saya sendiri, saya gagal:

struct MyValueTuple<A, B>
{
    public A Item1;
    public B Item2;

    public MyValueTuple(A item1, B item2)
    {
        Item1 = item1;
        Item2 = item2;
    }
}

...

MyValueTuple<string, string> s = new MyValueTuple<string, string>("a", "b");
MyValueTuple<object, string> o = s;
// ^ Cannot implicitly convert type 'MyValueTuple<string, string>' to 'MyValueTuple<object, string>'

Jadi, mengapa bisa ValueTupleditugaskan secara kovarian, tetapi MyValueTupletidak bisa?

Heinzi
sumber
2
Ini kemungkinan perlakuan khusus oleh kompiler, sama seperti bagaimana Anda dapat menetapkan nullke Nullable<T>meskipun itu sebuah struct.
juharr
2
Sebenarnya, kode yang didekompilasi terlihat seperti ini untuk tugas kedua:ValueTuple<object, string> o = new ValueTuple<object, string>(s.Item1, s.Item2);
Lasse V. Karlsen
2
Tuples aneh dan diimplementasikan seluruhnya di front end c # compiler, alih-alih mengandalkan representasi CLR yang mendasarinya. Operator penugasan itu tidak melakukan apa yang Anda pikirkan.
Jeremy Lakeman
2
Tambahkan operator implisit public static implicit operator MyValueTuple<A, B>(MyValueTuple<string, string> v) { throw new NotImplementedException(); }yang mendekonstruksi tugas tersebut. Omong-omong, pertanyaan yang bagus!
Çöđěxěŕ
1
@ Çöđěxěŕ bulls eye! yang membuatnya dapat dikompilasi, dan pengecualian dilemparkan seperti yang diharapkan
Mong Zhu

Jawaban:

25

Saya percaya apa yang sebenarnya terjadi di sini adalah tugas merusak. Tupel tugas akan mencoba untuk secara implisit mengkonversi komponennya, dan karena mungkin untuk menetapkan stringke object, itulah yang terjadi di sini.

Bahasa ini mendukung penugasan antara jenis tuple yang memiliki jumlah elemen yang sama, di mana setiap elemen sisi kanan dapat secara implisit dikonversi ke elemen sisi kiri yang sesuai. Konversi lain tidak dipertimbangkan untuk penugasan.

Sumber

Lihat di sharplab.io

Gareth Latty
sumber
4
Saya baru saja mencobanya di SharpLab dan cukup yakin itu tidak persis seperti itu .
John
3
Hanya untuk memperkuat itu, dalam contoh asli OP, jika Anda mengubah suntuk mengetiknya (string, object)menghasilkan kesalahan konversi, menunjukkan bahwa konversi implisit sedang terjadi di antara item, dan string dapat secara implisit dikonversi menjadi string, tetapi tidak sebaliknya.
Eric Lease