W C # dlaczego nie można przechowywać obiektu List w zmiennej List


Wygląda na to, że obiekt List nie może być przechowywany w zmiennej List w języku C # i nie można go nawet jawnie rzutować w ten sposób.
List<string> sl = new List<string>();
List<object> ol;
ol = sl;

wyniki nie mogą niejawnie konwertować typu
System.Collections.Generic.List & < string & >
na
System.Collections.Generic.List & < object & >
I wtedy...
List<string> sl = new List<string>();
List<object> ol;
ol = (List<object>)sl;

wyniki nie mogą konwertować typu
System.Collections.Generic.List & < string & >
na
System.Collections.Generic.List & < object & >
Oczywiście możesz to zrobić, wyciągając wszystko z listy ciągów i odkładając je pojedynczo, ale jest to dość mylące rozwiązanie.
Zaproszony:
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Pomyśl o tym w ten sposób: jeśli rzucisz w ten sposób, a następnie dodasz obiekt typu Foo do listy, lista łańcuchów nie będzie już spójna. Gdybyś powtórzył pierwsze odwołanie, dostałbyś wyjątek rzutowania, ponieważ po uderzeniu w instancję Foo, Foo nie może zostać przekonwertowane na łańcuch!
Na marginesie, myślę, że byłoby ważniejsze, gdybyś mógł wykonać tylną obsadę:
List<object> ol = new List<object>();
List<string> sl;
sl = (List<string>)ol;

Dawno nie używałem C #, więc nie wiem, czy jest to legalne, ale taka obsada jest faktycznie (potencjalnie) przydatna. W tym przypadku przechodzisz z bardziej ogólnej klasy (obiektu) do bardziej szczegółowej klasy (ciągu znaków), która wykracza poza klasę ogólną. W ten sposób, jeśli dodasz łańcuchy do listy, nie zepsujesz listy obiektów.
Czy ktoś wie lub może sprawdzić, czy taka obsada jest legalna w C #?
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Jeśli używasz .NET 3.5, przyjrzyj się metodzie Enumerable.Cast. Jest to metoda rozszerzająca, więc możesz ją wywołać bezpośrednio na liście.
List<string> sl = new List<string>();
IEnumerable<object> ol;
ol = sl.Cast<object>();

To nie jest dokładnie to, o co prosiłeś, ale powinno działać.
Edycja: jak zauważyła Zooba, możesz wywołać ol.ToList (), aby uzyskać listę
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Nie można rzutować między typami ogólnymi z różnymi parametrami typu. Wyspecjalizowane typy ogólne nie są częścią tego samego drzewa dziedziczenia i dlatego są typami niepowiązanymi.
Aby to zrobić, przed NET 3.5:
List<string> sl = new List<string>();
// Add strings to slList<object> ol = new List<object>();foreach(string s in sl)
{
ol.Add((object)s);// The cast is performed implicitly even if omitted
}

Korzystanie z Linq:
var sl = new List<string>();
// Add strings to slvar 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();
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Powodem jest to, że klasa ogólna, taka jak
List & < & >
, jest w większości przypadków wyświetlana zewnętrznie jako zwykła klasa. na przykład, kiedy powiesz
List & < string & > ()
, kompilator powie
ListString ()
(który zawiera ciągi). [Specjaliści od technologii: to jest skrajnie zrozumiała angielska wersja tego, co się dzieje]
W związku z tym jest oczywiste, że kompilator nie może być wystarczająco inteligentny, aby przekonwertować ListString na ListObject przez rzutowanie elementów swojej wewnętrznej kolekcji.
Dlatego istnieją metody rozszerzające dla IEnumerable, takie jak Convert (), które ułatwiają konwersję elementów przechowywanych w kolekcji, co może być tak proste, jak przejście z jednego do drugiego.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Ma to wiele wspólnego z kowariancją, na przykład typy ogólne są traktowane jako parametry, a jeśli parametry nie zostaną poprawnie rozwiązane dla bardziej konkretnego typu, operacja zakończy się niepowodzeniem. Wynika z tego, że naprawdę nie można rzutować na bardziej ogólny typ, taki jak obiekt. Jak stwierdził Rex, obiekt listy nie skonwertuje każdego obiektu za Ciebie.
Zamiast tego możesz wypróbować kod FF:
List<string> sl = new List<string>();
//populate sl
List<object> ol = new List<object>(sl);

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

ol będzie (teoretycznie) kopiować całą zawartość sl bez problemu.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Tak, możesz, zaczynając od .NET 3.5:
List<string> sl = new List<string>();
List<object> ol = sl.Cast<object>().ToList();
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Mike - uważam, że kontrawariancja jest również niedozwolona w C #
Więcej informacji znajdziesz w

Zobacz Wariancja parametrów typu ogólnego
http://blogs.msdn.com/rmbyers/ ... .aspx
w CLR.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Myślę, że ta (kontrawariancja) będzie rzeczywiście obsługiwana w C # 4.0.

http:// blogs.msdn.com/charlie/archive/2008/10/27/linq-farm-covariance-and-contravariation-in-visual-studio-2010.aspx
http://blogs.msdn.com/charlie/ ... .aspx
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

W rzeczywistości jest tak, aby nie próbować umieszczać żadnego dziwnego „obiektu” w swojej wersji listy „ol” (ponieważ wydaje się, że
List & < object & >
zezwala) - ponieważ wtedy twój kod ulegnie awarii (ponieważ lista jest rzeczywiście
List & < string & >
i akceptuje tylko obiekty typu string). Dlatego nie możesz rzutować swojej zmiennej na bardziej ogólną specyfikację.
W Javie jest odwrotnie, nie masz typów ogólnych, a zamiast tego wszystko jest listą obiektów w czasie wykonywania i możesz umieścić dowolny dziwny obiekt na swojej rzekomo mocno wpisanej liście. Wyszukaj „Reified generics”, aby zobaczyć szersze omówienie problemu z Javą ...
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Ten rodzaj kowariancji nie jest obsługiwany w przypadku typów ogólnych, ale w rzeczywistości można to zrobić za pomocą tablic:
object[] a = new string[] {"spam", "eggs"};

C # sprawdza w czasie wykonywania, więc nie można umieścić, powiedzmy,
int
w
a
.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Oto inne rozwiązanie poprzedzające .NET 3.5 dla każdego IList, którego zawartość może być rzutowana niejawnie.
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;
}

(Na przykładzie Zooba)
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

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

Zamierzałem wypełnić go danymi zebranymi w
List & < object & >
, co ostatecznie zadziałało, to:
Leerlingen = (List<Leerling>)_DeserialiseerLeerlingen._TeSerialiserenObjecten.Cast<Leerling>();

.Cast
jest typem, dla którego chcesz uzyskać
IEnumerable
z tego typu, a następnie wpisz
IEnemuerable
na liście
List & lt ; & >
, który chcesz.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Dzięki wcześniejszym komentarzom znalazłem dwa sposoby, aby to rozgryźć. Pierwszym jest pobranie listy elementów, a następnie rzutowanie jej na listę obiektów IEnumerable:
IEnumerable<object> ob;
List<string> st = new List<string>();
ob = st.Cast<object>();

Drugim jest uniknięcie typu obiektu IEnumerable, po prostu rzutowanie ciągu na typ obiektu, a następnie użycie funkcji „toList ()” w tym samym zdaniu:
List<string> st = new List<string>();
List<object> ob = st.Cast<object>().ToList();

Drugi sposób bardziej mi się podoba. Mam nadzieję że to pomoże.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

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

Aby odpowiedzieć na pytania, Zaloguj się lub Zarejestruj się