Bagaimana Generic Covariance & Contra-variance Diimplementasikan dalam C # 4.0?

106

Saya tidak menghadiri PDC 2008, tetapi saya mendengar beberapa berita bahwa C # 4.0 diumumkan untuk mendukung kovarians Generik dan kontra-varian. Artinya, List<string>bisa ditugaskan ke List<object>. Bagaimana bisa?

Dalam buku Jon Skeet C # in Depth , dijelaskan mengapa C # generik tidak mendukung kovariansi dan kontra-varian. Ini terutama untuk menulis kode aman. Sekarang, C # 4.0 berubah untuk mendukung mereka. Apakah itu akan membawa kekacauan?

Adakah yang tahu detail tentang C # 4.0 dapat memberikan penjelasan?

Morgan Cheng
sumber
Berikut adalah artikel bagus yang mencakup implementasi kovarians dan kontra-varians yang akan datang pada delegasi dan antarmuka di C # 4.0: LINQ Farm: Kovarian dan Kontravarian di C # 4.0
CMS
Anders Noråse menjelaskan dalam C # 4.0 - Kovarian dan kontra-varians konsep dan pertunjukan, bahwa itu sudah didukung hari ini di IL sejak .NET 2.0.
Thomas Freudenberg

Jawaban:

155

Varians hanya akan didukung dengan cara yang aman - pada kenyataannya, menggunakan kemampuan yang sudah dimiliki CLR. Jadi contoh yang saya berikan dalam buku tentang mencoba menggunakan a List<Banana>sebagai List<Fruit>(atau apa pun itu) tetap tidak akan berhasil - tetapi beberapa skenario lain akan berhasil.

Pertama, ini hanya akan didukung untuk antarmuka dan delegasi.

Kedua, memerlukan pembuat antarmuka / delegasi untuk menghias parameter tipe sebagai in(untuk kontradiksi) atau out(untuk kovarian). Contoh yang paling jelas adalah IEnumerable<T>yang hanya memungkinkan Anda mengambil nilai "keluar" darinya - tidak memungkinkan Anda menambahkan nilai baru. Itu akan menjadi IEnumerable<out T>. Itu tidak mengganggu keamanan tipe sama sekali, tetapi memungkinkan Anda mengembalikan IEnumerable<string>dari metode yang dideklarasikan untuk dikembalikan IEnumerable<object>misalnya.

Kontradiksi lebih sulit untuk memberikan contoh konkret untuk menggunakan antarmuka, tetapi mudah dengan delegasi. Pertimbangkan Action<T>- itu hanya mewakili metode yang mengambil Tparameter. Akan menyenangkan untuk dapat mengonversi dengan mulus menggunakan an Action<object>sebagai Action<string>- metode apa pun yang menggunakan objectparameter akan baik-baik saja ketika disajikan dengan a stringsebagai gantinya. Tentu saja, C # 2 sudah memiliki kovariansi dan kontradiksi delegasi sampai batas tertentu, tetapi melalui konversi aktual dari satu jenis delegasi ke jenis lainnya (membuat instans baru) - lihat P141-144 sebagai contoh. C # 4 akan membuat ini lebih umum, dan (saya percaya) akan menghindari membuat contoh baru untuk konversi. (Alih-alih, ini akan menjadi konversi referensi.)

Semoga ini sedikit menyelesaikannya - beri tahu saya jika tidak masuk akal!

Jon Skeet
sumber
3
Jadi, apakah itu berarti bahwa jika kelas tersebut dideklarasikan sebagai "List <out T>" maka TIDAK harus memiliki fungsi anggota seperti "void Add (T obj)"? Kompilator C # 4.0 akan melaporkan kesalahan itu, bukan?
Morgan Cheng
1
Morgan: Itu pasti pemahaman saya, ya.
Jon Skeet
4
sekali lagi salah satu jawaban Anda di SO telah segera membantu saya meningkatkan beberapa kode. Terima kasih!
Tandai
@ Ark-kun: Ya, saya tahu itu. Karenanya, "masih tidak akan berfungsi" dalam kalimat yang sama. (Dan saya juga mengetahui alasannya.)
Jon Skeet
@JonSkeet Apakah benar bahwa "Anda hanya dapat menggunakan List<Banana>sebagai IList<Fruit>" seperti yang dikatakan @ Ark-kun? Jika demikian, bagaimana ini mungkin, meskipun parameter tipe IList<T>antarmuka tidak didefinisikan sebagai kovarian (tidak out T, tetapi sederhana T).
gehho