Saya punya private readonly
daftar LinkLabel
s ( IList<LinkLabel>
). Saya kemudian menambahkan LinkLabel
s ke daftar ini dan menambahkan label-label itu ke FlowLayoutPanel
seperti berikut:
foreach(var s in strings)
{
_list.Add(new LinkLabel{Text=s});
}
flPanel.Controls.AddRange(_list.ToArray());
Menunjukkan Resharper saya peringatan: Co-variant array conversion from LinkLabel[] to Control[] can cause run-time exception on write operation
.
Tolong bantu saya untuk mencari tahu:
- Apa artinya ini?
- Ini adalah kontrol pengguna dan tidak akan diakses oleh banyak objek untuk men-setup label, jadi menjaga kode seperti itu tidak akan mempengaruhinya.
sumber
LinkLabel
(tipe khusus) keControl
(tipe dasar).LinkLabel[]
keControl[]
, yang masih legal, tetapi dapat memiliki masalah runtime. Semua yang telah berubah adalah cara array sedang direferensikan. Array itu sendiri tidak berubah. Lihat masalahnya? Array masih merupakan array dari tipe turunan. Referensi adalah melalui array dari tipe dasar. Oleh karena itu, kompilasi waktu legal untuk menetapkan elemen ke tipe dasar. Namun tipe runtime tidak akan mendukungnya.Saya akan mencoba menjelaskan jawaban Anthony Pegram.
Tipe generik adalah kovarian pada beberapa argumen tipe ketika ia mengembalikan nilai-nilai dari tipe tersebut (misalnya
Func<out TResult>
mengembalikan instance dariTResult
,IEnumerable<out T>
mengembalikan instance dariT
). Artinya, jika sesuatu mengembalikan instanceTDerived
, Anda juga dapat bekerja dengan instance seperti seolah-olah berasal dariTBase
.Tipe generik bersifat contravarian pada beberapa argumen tipe ketika ia menerima nilai dari tipe tersebut (mis.
Action<in TArgument>
Menerima instance dariTArgument
). Artinya, jika sesuatu membutuhkan contohTBase
, Anda juga dapat lulus dalam contohTDerived
.Tampaknya cukup logis bahwa tipe generik yang menerima dan mengembalikan instance dari beberapa tipe (kecuali jika didefinisikan dua kali dalam tanda tangan tipe generik, misalnya
CoolList<TIn, TOut>
) tidak kovarian atau kontravarian pada argumen tipe yang sesuai. Misalnya,List
didefinisikan dalam .NET 4 sebagaiList<T>
, bukanList<in T>
atauList<out T>
.Beberapa alasan kompatibilitas mungkin menyebabkan Microsoft mengabaikan argumen itu dan membuat array kovarian pada argumen tipe nilai mereka. Mungkin mereka melakukan analisis dan menemukan bahwa kebanyakan orang hanya menggunakan array seolah-olah hanya dibaca (yaitu, mereka hanya menggunakan inisialisasi array untuk menulis beberapa data ke dalam array), dan, dengan demikian, kelebihannya melebihi kerugian yang disebabkan oleh kemungkinan runtime kesalahan ketika seseorang akan mencoba memanfaatkan kovarians saat menulis ke dalam array. Oleh karena itu diperbolehkan tetapi tidak dianjurkan.
Adapun pertanyaan asli Anda,
list.ToArray()
menciptakan baruLinkLabel[]
dengan nilai-nilai disalin dari daftar asli, dan, untuk menyingkirkan (wajar) peringatan, Anda harus lulus dalamControl[]
untukAddRange
.list.ToArray<Control>()
akan melakukan pekerjaan:ToArray<TSource>
menerimaIEnumerable<TSource>
sebagai argumennya dan kembaliTSource[]
;List<LinkLabel>
mengimplementasikan read-onlyIEnumerable<out LinkLabel>
, yang, berkatIEnumerable
kovarian, dapat diteruskan ke metode menerimaIEnumerable<Control>
sebagai argumennya.sumber
"Solusi" paling lurus ke depan
flPanel.Controls.AddRange(_list.AsEnumerable());
Sekarang karena Anda secara kovarian berubah
List<LinkLabel>
menjadiIEnumerable<Control>
tidak ada lagi kekhawatiran karena tidak mungkin untuk "menambahkan" item ke yang dapat dihitung.sumber
Peringatan ini disebabkan oleh kenyataan bahwa Anda secara teoritis bisa menambahkan
Control
selainLinkLabel
untukLinkLabel[]
melaluiControl[]
referensi untuk itu. Ini akan menyebabkan pengecualian runtime.Konversi terjadi di sini karena
AddRange
memerlukan aControl[]
.Lebih umum, mengubah wadah dari tipe turunan ke wadah dari tipe dasar hanya aman jika Anda tidak dapat memodifikasi wadah dengan cara yang baru saja dijelaskan. Array tidak memenuhi persyaratan itu.
sumber
Akar penyebab masalah dijelaskan dengan benar dalam jawaban lain, tetapi untuk menyelesaikan peringatan, Anda selalu dapat menulis:
sumber
Dengan VS 2008, saya tidak mendapatkan peringatan ini. Ini harus baru untuk .NET 4.0.
Klarifikasi: menurut Sam Mackrill, Resharper yang menampilkan peringatan.
Compiler C # tidak tahu bahwa
AddRange
tidak akan memodifikasi array yang diteruskan ke sana. KarenaAddRange
memiliki parameter tipeControl[]
, secara teori bisa mencoba untuk menetapkan sebuahTextBox
ke array, yang akan benar untuk array yang benarControl
, tetapi sebenarnya array adalah arrayLinkLabels
dan tidak akan menerima tugas seperti itu.Membuat array co-varian dalam c # adalah keputusan yang buruk dari Microsoft. Meskipun mungkin tampak ide yang bagus untuk dapat menetapkan array dari tipe turunan ke array dari tipe dasar, ini dapat menyebabkan kesalahan runtime!
sumber
Bagaimana dengan ini?
sumber
_list.ToArray<Control>()
.