C # 'da, List <string> nesnesi List <object> değişkeninde neden depolanamıyor?


85

Görünüşe göre bir List nesnesi C # 'da bir List değişkeninde saklanamaz ve hatta açıkça bu şekilde değiştirilemez.

List<string> sl = new List<string>();
List<object> ol;
ol = sl;

sonuç türü örtük System.Collections.Generic.List<string>olarakSystem.Collections.Generic.List<object>

Ve sonra...

List<string> sl = new List<string>();
List<object> ol;
ol = (List<object>)sl;

Can sonuçları değil türünü dönüştürmek System.Collections.Generic.List<string>içinSystem.Collections.Generic.List<object>

Elbette, her şeyi dizgi listesinden çıkararak ve her seferinde bir tane geri koyarak yapabilirsiniz, ancak bu oldukça karmaşık bir çözümdür.


5
Bu C # 4.0 ile değişecek, dolayısıyla kovaryans ve kontravans aramak isteyebilirsiniz. Bu tür şeylere güvenli bir şekilde izin verecektir.
John Leidegren


7
John> C # 4 buna izin vermeyecek. Ol.Add (new object ());
Guillaume

Burada Jon Skeet tarafından cevaplandı: stackoverflow.com/a/2033921/1070906
JCH2k

Yanıtlar:


39

Bu şekilde düşünün, eğer böyle bir atama yapacaksanız ve ardından listeye Foo türünde bir nesne eklediyseniz, dizelerin listesi artık tutarlı değildir. İlk referansı yinelerseniz, bir sınıf atama istisnası alırsınız çünkü Foo örneğine bir kez bastığınızda, Foo dizeye dönüştürülemez!

Bir yan not olarak, tersine atışı yapıp yapamayacağınızın daha önemli olacağını düşünüyorum:

List<object> ol = new List<object>();
List<string> sl;
sl = (List<string>)ol;

Bir süredir C # kullanmadım, bu yüzden bunun yasal olup olmadığını bilmiyorum, ancak bu tür bir döküm aslında (potansiyel olarak) yararlıdır. Bu durumda, daha genel bir sınıftan (nesne), genel olandan uzanan daha özel bir sınıfa (dizge) geçersiniz. Bu şekilde, dizeler listesine eklerseniz, nesne listesini ihlal etmiş olmazsınız.

C # 'da böyle bir aletin yasal olup olmadığını bilen veya test edebilen var mı?


4
Sanırım sizin örneğiniz de aynı sorundan muzdarip. Ol içinde dizge olmayan bir şey varsa ne olur? Sorun, Listedeki bazı yöntemlerin, ekleme / ekleme gibi iyi çalışacak olmasıdır. Ancak yineleme gerçek bir sorun olabilir.
Chris Ammerman

3
Eric Lippert'in bu konuyla ilgili harika bir dizi blog yazısı var: neden genel yöntemlere kovaryans ve kontraverans kısıtlamaları eklemek işe yarayabilir, ancak asla sınıf düzeyinde istediğimiz şekilde çalışmayabilir. is.gd/3kQc
Chris Ammerman

@ChrisAmmerman: İçinde oldize olmayan bir şey olsaydı, sanırım cast'ın çalışma zamanında başarısız olmasını beklerdim. Ama gerçekten sorunla karşılaşacağınız yer, eğer oyuncu kadrosu başarılı olursa ve sonraol bir dizi olmayan bir şey eklenirse . Çünkü slreferanslar aynı nesne, şimdi senin List<string>olmayan bir dize içerecektir. AddBu kod derlemek olmaz neden haklı çıkarmaktadır tahmin problem, ancak değiştirme eğer derlemek olacak List<object> olkadar IEnumerable<object> olbir yok olan Add. (Bunu C # 4'te kontrol ettim.)
Tim Goodman

Bu değişiklikle birlikte InvalidCastException, çalışma zamanı türü olhala olduğu için derler ancak bir atar List<object>.
Tim Goodman

36

.NET 3.5 kullanıyorsanız Enumerable.Cast yöntemine bakın. Bu bir uzantı yöntemidir, böylece onu doğrudan Listeden çağırabilirsiniz.

List<string> sl = new List<string>();
IEnumerable<object> ol;
ol = sl.Cast<object>();

Tam olarak istediğiniz şey değil ama hile yapmalısınız.

Düzenleme: Zooba tarafından belirtildiği gibi, bir Liste almak için ol.ToList () öğesini çağırabilirsiniz.


14

Farklı tür parametrelerine sahip genel türler arasında dönüşüm yapamazsınız. Özelleştirilmiş genel türler, aynı miras ağacının bir parçasını oluşturmaz ve bu nedenle ilgisiz türlerdir.

Bunu NET 3.5 öncesi yapmak için:

List<string> sl = new List<string>();
// Add strings to sl

List<object> ol = new List<object>();

foreach(string s in sl)
{
    ol.Add((object)s);  // The cast is performed implicitly even if omitted
}

Linq kullanarak:

var sl = new List<string>();
// Add strings to sl

var ol = new List<object>(sl.Cast<object>());

// OR
var ol = sl.Cast<object>().ToList();

// OR (note that the cast to object here is required)
var ol = sl.Select(s => (object)s).ToList();

11

Bunun nedeni, benzeri genel bir sınıfın List<>, çoğu amaç için, dışarıdan normal bir sınıf olarak değerlendirilmesidir. Örneğin List<string>(), derleyicinin ListString()(dizeleri içeren) dediğini söylediğinizde . [Teknik uzman: Bu, olup bitenlerin son derece sade bir İngilizce ifadesidir]

Sonuç olarak, derleyici, iç koleksiyonunun öğelerini çevirerek bir ListString'i ListObject'e dönüştürmek için yeterince akıllı olamaz.

Bu nedenle, IEnumerable için Convert () gibi bir koleksiyon içinde depolanan öğeler için kolayca dönüştürme sağlamanıza izin veren genişletme yöntemleri vardır; bu, birinden diğerine dönüştürme kadar basit olabilir.


6

Bunun kovaryansla çok ilgisi vardır, örneğin, jenerik tipler parametre olarak kabul edilir ve eğer parametreler daha spesifik bir türe uygun şekilde çözülemezse işlem başarısız olur. Bunun anlamı, nesneye benzer daha genel bir türe gerçekten dönüşemeyeceğinizdir. Ve Rex'in belirttiği gibi, List nesnesi her nesneyi sizin için dönüştürmez.

Bunun yerine ff kodunu denemek isteyebilirsiniz:

List<string> sl = new List<string>();
//populate sl
List<object> ol = new List<object>(sl);

veya:

List<object> ol = new List<object>();
ol.AddRange(sl);

ol, sl'nin tüm içeriğini (teorik olarak) sorunsuz bir şekilde kopyalayacaktır.


5

Evet, .NET 3.5'ten şunları yapabilirsiniz:

List<string> sl = new List<string>();
List<object> ol = sl.Cast<object>().ToList();



1

Aslında bu, "ol" liste varyantınıza herhangi bir tuhaf "nesne" koymaya çalışmamanız için ( List<object>izin verdiği gibi) - çünkü o zaman kodunuz çöker (çünkü liste gerçekten List<string>ve sadece String türü nesneleri kabul eder) ). Bu yüzden değişkeninizi daha genel bir spesifikasyona çeviremezsiniz.

Java'da ise tam tersi, jeneriklere sahip değilsiniz ve bunun yerine her şey çalışma zamanında nesne listesidir ve sözde kesinlikle yazılmış Listenizdeki herhangi bir tuhaf nesneyi gerçekten doldurabilirsiniz. Java'nın sorunu hakkında daha geniş bir tartışma görmek için "Reified jenerikler" arayın ...


1

Jenerikler üzerindeki bu tür kovaryans desteklenmez, ancak bunu dizilerle yapabilirsiniz:

object[] a = new string[] {"spam", "eggs"};

C #, örneğin intiçine girmenizi önlemek için çalışma zamanı kontrolleri gerçekleştirir a.


0

İşte içeriği örtük olarak dönüştürülebilen herhangi bir IList için başka bir pre-.NET 3.5 çözümü.

public IList<B> ConvertIList<D, B>(IList<D> list) where D : B
{
    List<B> newList = new List<B>();

    foreach (D item in list)
    {
        newList.Add(item);
    }

    return newList;
}

(Zooba'nın örneğine göre)


0

Bende:

private List<Leerling> Leerlingen = new List<Leerling>();

Ve onu toplanan verilerle dolduracaktım. List<object> Sonunda benim için işe yarayan şuydu:

Leerlingen = (List<Leerling>)_DeserialiseerLeerlingen._TeSerialiserenObjecten.Cast<Leerling>();

.Casto türe bir almak istiyorum IEnumerablesonra kalıplaştığı, bu tip IEnemuerableiçin List<>istediğiniz.


0

Mm, önceki yorumlar sayesinde bunu öğrenmenin iki yolunu buldum. İlki, elemanların dize listesini almak ve ardından bunu IEnumerable nesne listesine çevirmektir:

IEnumerable<object> ob;
List<string> st = new List<string>();
ob = st.Cast<object>();

İkincisi, IEnumerable nesne türünden kaçınmak, dizeyi nesne türüne dönüştürmek ve ardından aynı cümlede "toList ()" işlevini kullanmaktır:

List<string> st = new List<string>();
List<object> ob = st.Cast<object>().ToList();

İkinci yolu daha çok seviyorum. Umarım bu yardımcı olur.


0
List<string> sl = new List<string>();
List<object> ol;
ol = new List<object>(sl);
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.