Wygląda na to, że obiekt List nie może być przechowywany w zmiennej List w C #, a nawet nie może być jawnie rzutowany w ten sposób.
List<string> sl = new List<string>();
List<object> ol;
ol = sl;
powoduje, że nie można niejawnie przekonwertować typu System.Collections.Generic.List<string>
naSystem.Collections.Generic.List<object>
I wtedy...
List<string> sl = new List<string>();
List<object> ol;
ol = (List<object>)sl;
powoduje, że nie można przekonwertować typu System.Collections.Generic.List<string>
naSystem.Collections.Generic.List<object>
Oczywiście możesz to zrobić, wyciągając wszystko z listy ciągów i umieszczając je z powrotem po jednym, ale jest to dość zawiłe rozwiązanie.
c#
.net
generics
covariance
type-safety
Matt Sheppard
źródło
źródło
Odpowiedzi:
Pomyśl o tym w ten sposób, jeśli miałbyś wykonać takie rzutowanie, a następnie dodać obiekt typu Foo do listy, lista łańcuchów nie jest już spójna. Gdybyś miał iterować pierwsze odwołanie, dostałbyś wyjątek rzutowania klasy, ponieważ po trafieniu na instancję Foo, Foo nie może zostać przekonwertowane na łańcuch!
Na marginesie, myślę, że byłoby bardziej istotne, czy możesz wykonać obsadę odwrotną:
List<object> ol = new List<object>(); List<string> sl; sl = (List<string>)ol;
Od jakiegoś czasu nie używałem C #, więc nie wiem, czy to jest legalne, ale ten rodzaj rzutowania jest faktycznie (potencjalnie) przydatny. W tym przypadku przechodzisz od bardziej ogólnej klasy (obiektu) do bardziej szczegółowej klasy (łańcucha), która rozciąga się od ogólnej. W ten sposób, jeśli dodajesz do listy ciągów, nie naruszasz listy obiektów.
Czy ktoś wie lub może sprawdzić, czy taka obsada jest legalna w C #?
źródło
ol
było w nim coś, co nie jest ciągiem, przypuszczam, że spodziewałbym się niepowodzenia rzutowania w czasie wykonywania. Ale naprawdę miałbyś kłopoty, gdyby rzut się powiódł, a potem dodano cośol
, co nie jest ciągiem. Ponieważsl
odwołuje się do tego samego obiektu, teraz twójList<string>
będzie zawierał inny niż ciąg. ToAdd
jest problem, który, jak sądzę, uzasadnia, dlaczego ten kod się nie kompiluje, ale skompiluje się, jeśli zmieniszList<object> ol
naIEnumerable<object> ol
, który nie ma rozszerzeniaAdd
. (Sprawdziłem to w C # 4)InvalidCastException
ponieważ typ środowiska wykonawczegool
jest nadalList<object>
.Jeśli używasz .NET 3.5, zapoznaj się z metodą Enumerable.Cast. Jest to metoda rozszerzenia, 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>();
Nie jest to dokładnie to, o co prosiłeś, ale powinno załatwić sprawę.
Edycja: jak zauważyła Zooba, możesz następnie wywołać ol.ToList (), aby uzyskać listę
źródło
Nie można rzutować między typami ogólnymi z różnymi parametrami typu. Wyspecjalizowane typy ogólne nie stanowią części tego samego drzewa dziedziczenia, a więc są to typy niepowiązane.
Aby to zrobić przed NET 3.5:
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 }
Korzystanie z Linq:
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();
źródło
Powodem jest to, że klasa ogólna, taka jak,
List<>
jest w większości celów traktowana zewnętrznie jako klasa normalna. np. kiedy mówisz,List<string>()
że kompilator mówiListString()
(który zawiera ciągi znaków). [Techniczni ludzie: to jest skrajnie zrozumiała angielska wersja tego, co się dzieje]W związku z tym oczywiście kompilator nie może być wystarczająco inteligentny, aby przekonwertować ListString na ListObject przez rzutowanie elementów jego wewnętrznej kolekcji.
Dlatego istnieją metody rozszerzające dla IEnumerable, takie jak Convert (), które umożliwiają łatwe przekształcanie elementów przechowywanych w kolekcji, co może być tak proste, jak rzutowanie między nimi.
źródło
Ma to wiele wspólnego z kowariancją, np. Typy ogólne są uważane za parametry, a jeśli parametry nie są poprawnie rozwiązywane na bardziej szczegółowy typ, operacja kończy się niepowodzeniem. Konsekwencją takiego rozwiązania jest to, że naprawdę nie można rzutować na bardziej ogólny typ, taki jak obiekt. Jak stwierdził Rex, obiekt List 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 bez problemu skopiuje całą zawartość sl.
źródło
Tak, możesz od .NET 3.5:
List<string> sl = new List<string>(); List<object> ol = sl.Cast<object>().ToList();
źródło
Mike - uważam, że kontrawariancja nie jest dozwolona w C #
Aby uzyskać więcej informacji, zobacz Wariancja parametrów typu ogólnego w środowisku CLR .
źródło
Myślę, że ta (kontrawariancja) będzie faktycznie obsługiwana w C # 4.0. http://blogs.msdn.com/charlie/archive/2008/10/27/linq-farm-covariance-and-contravariance-in-visual-studio-2010.aspx
źródło
To jest właściwie, aby nie próbować umieszczać żadnego dziwnego "obiektu" w swoim wariancie listy "ol" (jak
List<object>
mogłoby się wydawać) - ponieważ wtedy twój kod by się wywalił (ponieważ lista naprawdę jestList<string>
i będzie akceptować 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, a naprawdę możesz umieścić dowolny dziwny obiekt na swojej rzekomo ściśle wpisanej Liście. Wyszukaj „Reified generics”, aby zobaczyć szerszą dyskusję na temat problemu Java ...
źródło
Taka kowariancja w przypadku typów ogólnych nie jest obsługiwana, ale w rzeczywistości można to zrobić z tablicami:
object[] a = new string[] {"spam", "eggs"};
C # wykonuje testy w czasie wykonywania, aby zapobiec umieszczaniu, powiedzmy,
int
doa
.źródło
Oto inne rozwiązanie pre- .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 podstawie przykładu Zooby)
źródło
Mam:
private List<Leerling> Leerlingen = new List<Leerling>();
Miałem zamiar wypełnić go danymi zebranymi w tym,
List<object>
co w końcu zadziałało dla mnie:.Cast
to od rodzaju chcesz uzyskaćIEnumerable
z tego typu, a następnie typecastIEnemuerable
doList<>
chcesz.źródło
Mm, dzięki wcześniejszym komentarzom znalazłem dwa sposoby, aby się tego dowiedzieć. Pierwszym z nich 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 unikanie typu obiektu IEnumerable, po prostu rzutowanie ciągu na typ obiektowy, 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.
źródło
List<string> sl = new List<string>(); List<object> ol; ol = new List<object>(sl);
źródło