X'den y'ye eş varyant dizi dönüşümü, çalışma zamanı istisnasına neden olabilir


142

Bir s ( ) private readonlylistem var . Daha sonra bu listeye s ve bu etiketleri aşağıdaki gibi ekleyin :LinkLabelIList<LinkLabel>LinkLabelFlowLayoutPanel

foreach(var s in strings)
{
    _list.Add(new LinkLabel{Text=s});
}

flPanel.Controls.AddRange(_list.ToArray());

Resharper gösterileri Bana bir uyarı: Co-variant array conversion from LinkLabel[] to Control[] can cause run-time exception on write operation.

Lütfen anlamama yardımcı olun:

  1. Bu ne anlama geliyor?
  2. Bu bir kullanıcı denetimidir ve etiketleri ayarlamak için birden fazla nesne tarafından erişilmez, bu nedenle kodu böyle tutmak onu etkilemez.

Yanıtlar:


154

Anlamı bu

Control[] controls = new LinkLabel[10]; // compile time legal
controls[0] = new TextBox(); // compile time legal, runtime exception

Ve daha genel anlamda

string[] array = new string[10];
object[] objs = array; // legal at compile time
objs[0] = new Foo(); // again legal, with runtime exception

C # 'da, bir nesne dizisine (sizin durumunuzda, LinkLabels) temel türden bir dizi (bu durumda, bir Denetimler dizisi olarak) başvurmanıza izin verilir. Diziye bir olan başka bir nesneyi atamak yasaldır Control. Sorun dizinin aslında bir Kontroller dizisi olmamasıdır. Çalışma zamanında, hala bir LinkLabels dizisidir. Bu nedenle, atama veya yazma bir istisna atar.


Örnekte olduğu gibi çalışma zamanı / derleme zaman farkını anlıyorum, ancak özel türden taban türüne dönüşüm yasal değil mi? Ayrıca ben liste yazdım ve LinkLabel(uzman türü) Control(baz türü) gidiyorum .
TheVillageIdiot

2
Evet, LinkLabel'den Kontrole dönüştürmek yasaldır, ancak burada olanlarla aynı değildir. Bu dönüştürme konusunda uyarıyor LinkLabel[]için Control[]hala yasal olan, ancak bir çalışma zamanı sorunu olabilir. Değişen tek şey diziye referans verme şeklidir. Dizinin kendisi değişmez. Sorunu görüyor musunuz? Dizi hala türetilmiş türde bir dizidir. Referans, temel türden bir dizi üzerinden yapılır. Bu nedenle, taban türüne bir öğe atamak yasaldır. Ancak çalışma zamanı türü bunu desteklemez.
Anthony Pegram

Sizin durumunuzda, bunun bir sorun olduğunu düşünmüyorum, sadece kontrol listesine eklemek için diziyi kullanıyorsunuz.
Anthony Pegram

6
Herkes dizilerin neden C # 'da yanlış bir şekilde kovaryant olduğunu merak ederse, Eric Lippert'in açıklaması şöyledir : Java bunu gerektirdiği
franssu

14

Anthony Pegram'ın cevabını açıklığa kavuşturmaya çalışacağım.

Söz konusu türdeki değerleri (örn döndürdüğünde genel tip bir tür bağımsız değişkeni bildirdiğinden olan Func<out TResult>geri dönüşü örneklerini TResult, IEnumerable<out T>bir döner örneklerini T). Yani, bir şey örneklerini döndürürse TDerived, olduğu gibi örneklerle de çalışabilirsiniz TBase.

Genel tür, söz konusu türün değerlerini kabul ettiğinde (örneğin Action<in TArgument>örneklerini kabul ettiğinde) bazı tür bağımsız değişkenlerinde çelişkilidir TArgument. Yani, bir şeyin örneklere ihtiyacı varsa, örneklerden TBasede geçebilirsiniz TDerived.

Bazı türlerin örneklerini hem kabul eden hem de veren genel türlerin (örneğin genel tür imzasında iki kez tanımlanmadığı sürece CoolList<TIn, TOut>) karşılık gelen tür argümanında kovaryant veya kontravaryant olmadığı oldukça mantıklı görünmektedir . Örneğin, List.NET 4 tanımlanır List<T>değildir, List<in T>ya da List<out T>.

Bazı uyumluluk nedenleri Microsoft'un bu bağımsız değişkeni görmezden gelmesine ve dizileri değer türü bağımsız değişkenleri üzerinde kovaryant haline getirmesine neden olmuş olabilir. Belki bir analiz yaptılar ve çoğu insanın dizileri sadece salt okunurmuş gibi kullandıklarını (yani, bir diziye bazı verileri yazmak için sadece dizi başlatıcıları kullandıklarını) ve bu nedenle avantajların olası çalışma süresinin neden olduğu dezavantajları aştığını tespit ettiler. diziye yazarken bir kovaryans kullanmaya çalıştığında hatalar. Bu nedenle izin verilir, ancak teşvik edilmez.

Orijinal soruya gelince, list.ToArray()yeni oluşturur LinkLabel[](makul) uyarı kurtulmak için, orijinal listeden kopyalanan değerlerle ve, içeri geçmesi gerekir Control[]için AddRange. list.ToArray<Control>()işi yapacak: argümanı olarak ToArray<TSource>kabul eder IEnumerable<TSource>ve geri döner TSource[]; kovaryans sayesinde argüman olarak kabul edilen yönteme aktarılabilen List<LinkLabel>salt okunur uygulamaları gerçekleştirir .IEnumerable<out LinkLabel>IEnumerableIEnumerable<Control>


11

En düz ileri "çözüm"

flPanel.Controls.AddRange(_list.AsEnumerable());

Şimdi kovaryant olarak değiştirdiğiniz List<LinkLabel>için IEnumerable<Control>daha fazla endişe yok çünkü bir numaralandırmaya bir öğeyi "eklemek" mümkün değil.


10

Uyarı dolayı teorik bir ekleyebilir gerçeğine olan Controlbir başka LinkLabelüzere LinkLabel[]aracılığıyla Control[]kendisine atfen. Bu, çalışma zamanı istisnasına neden olur.

Burada dönüşüm gerçekleşiyor çünkü AddRangea Control[].

Daha genel olarak, türetilmiş tipteki bir kabı baz tipli bir konteynere dönüştürmek, ancak kapları daha sonra özetlenen şekilde değiştiremezseniz güvenlidir. Diziler bu gereksinimi karşılamıyor.


5

Sorunun kök nedeni diğer yanıtlarda doğru bir şekilde açıklanmıştır, ancak uyarıyı çözmek için her zaman yazabilirsiniz:

_list.ForEach(lnkLbl => flPanel.Controls.Add(lnkLbl));

2

VS 2008 ile bu uyarıyı almıyorum. Bu, .NET 4.0 için yeni olmalıdır.
Açıklama: Sam Mackrill'e göre bir uyarı gösteren Resharper.

C # derleyicisi AddRangekendisine geçirilen diziyi değiştirmeyeceğini bilmiyor . Yana AddRangetüründe bir parametresi vardır Control[], bu teoride bir atamak deneyebilirsiniz TextBoxgerçek dizisi için mükemmel doğru olacaktır tadını Controlama dizi gerçekte bir dizidir LinkLabelsve bu tür bir görevi kabul etmez.

Dizileri c # 'da birlikte varyant yapmak, Microsoft'un kötü bir karardı. İlk etapta temel türden bir diziye türetilmiş türden bir dizi atamak iyi bir fikir gibi görünse de bu, çalışma zamanı hatalarına yol açabilir!


2
Bu uyarıyı Resharper'dan
aldım

1

Buna ne dersin?

flPanel.Controls.AddRange(_list.OfType<Control>().ToArray());

2
İle aynı sonuç _list.ToArray<Control>().
jsuddsjr
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.