2015-02-13 21 views
15

Bir değer türü için bir arabirim uygular ve bunu bir arabirim türünün bir Listesi'ne göndermeyi denerim, bu durum bir hatayla sonuçlanırken, referans türü yalnızca iyi sonuçlanır? Ben açık bir şekilde neden dönüştürmek için Cast<T> yöntemi kullanmak zorundaToList <Interface> neden değer türleri için çalışmıyor?

Cannot convert instance argument type System.Collections.Generic.List<MyValueType> to System.Collections.Generic.IEnumerable<MyInterfaceType>

:

Bu

hatadır? IEnumerable bir koleksiyon aracılığıyla yapılan bir sayım sayımı olduğundan, doğrudan alınamayacağımı anlamıyorum.

Burada sorunu göstermek için örnek kod verilmiştir:

public interface I{} 

    public class T : I{} 

    public struct V: I{} 

    public void test() 
    { 
     var listT = new List<T>(); 
     var listV = new List<V>(); 

     var listIT = listT.ToList<I>();  //OK 
     var listIV = listV.ToList<I>();  //FAILS to compile, why? 

     var listIV2 = listV.Cast<I>().ToList(); //OK 

    } 
+8

Varyans yapıları için çalışmaz. Varyans referans türleri için çalışır, çünkü temsiller (referanslar/işaretçiler) bit kalıplarını korurlar. –

+0

@TheodorosChatzigiannakis contravariance olmalıdır, varyansı değil: http://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29 – Fals

+6

@Fals: Ne tür bir varyans değer türleri için çalışır. – SLaks

cevap

13

Variance (covariance or contravariance) değer türleri, sadece referans tipleri için çalışmaz: Referans tip değişkenler içinde bulunan

Variance applies only to reference types; if you specify a value type for a variant type parameter, that type parameter is invariant for the resulting constructed type. (MSDN)

değerleri için referanslar (vardır örnek, adresler) ve veri adresleri aynı boyuta sahiptir ve bit düzenlerinde herhangi bir değişiklik yapılmadan aynı şekilde yorumlanır. Buna karşılık, değer türü değişkenlerinin içerdiği değerler aynı büyüklükte veya aynı semantiğe sahip değildir. Bunları referans türleri olarak kullanmak boks'u gerektirir ve boks, derleyici tarafından yayılacak şekilde özel talimatlar gerektirir. Derleyici için olası herhangi bir değer türü için boks talimatı vermesi pratik veya verimli değildir (bazen mümkün değildir), dolayısıyla varyansa tamamen izin verilmez.

Temel olarak, varyans, değişkenden gerçek verilere olan fazladan referans (referans) katmanı sayesinde pratiktir. Değer türleri dolaylı olarak bu katmandan yoksun olduğundan, varyans yeteneklerinden yoksundurlar.


LINQ işlemleri nasıl çalıştığını yukarıda birleştirin:

Cast operasyon upcasts/kutuları ve daha sonra (eğer belirttiği gibi, genel olmayan IEnumerable aracılığıyla erişerek) tüm unsurları tüm unsurları olduğunu doğrulayan bir Bir sırayla sağlanan tipte başarılı bir şekilde dökme/kutudan çıkarılabilir ve tam olarak bunu yapabilir. ToList işlemi diziyi numaralandırır ve bu numaralandırmadan bir liste döndürür.

Her birinin kendi işi vardır. ToList (ikisi de), her ikisinin de işini yapsaydı, her iki performans performansına da sahip olacaktı, bu da diğer çoğu durum için istenmeyen bir durumdu.

+3

Desteklendi. Ek bilgi: 'ToList ' uzantısı "on" IEnumerable 'olarak tanımlanmıştır ve burada eşdeğer olan IEnumerable ' dir. Asker, aynı kovaryans nedeniyle IEnumerable listAsI = listT; 'yi de yapabilir, ancak bu (sığ) kopyayı gerçekleştirmeyecektir. –

+0

@JeppeStigNielsen Çalışıyor! Ama şimdi kayboldum: 'IEnumerable 'önceden atadığımda neden çalışıyor? Kovaryans IENumerable 'a atama için çalışıyorsa, neden dolaylı olarak .ToList ()' için çalışmıyor? – Marwie

+0

Ve ek olarak, bunun neden gerçekte işe yaradığını merak ediyorum, Theodoros Chatzigiannakis'in cevabında bahsettiği şey (değer türleri için bir farklılık yok) göz önüne alındığında bunun tam olarak bu durumda çalışmadığını düşünürdüm. – Marwie

İlgili konular