Ostatnio znalazłem bardzo zaskakujące zachowanie w języku C#. Miałem metodę, która przyjmuje jako parametr IEnumerable<Object>
i przechodziłem IEnumerable<string>
, ale nie jest to możliwe. Podczas gdy w języku C# wszystko może być upcast do Object niż dlaczego nie jest to możliwe? To dla mnie kompletnie zagmatwane. Proszę, oczyść mnie z tego problemu.Przesyłanie z IEnumerable <Object> do IEnumerable <string>
Odpowiedz
Technicznym terminem jest to, że generics są niezmienne w C# 3.0 i wcześniejszych. Od wersji C# 4.0 obsada działa.
Co niezmienne oznacza, że nie ma związku między dwoma typami rodzajowymi tylko dlatego, że ich parametry rodzajowe są powiązane (tj. Są podrzędne lub nadrzędne względem siebie).
W tym przykładzie nie ma żadnej relacji między typem IEnumerable<object>
i IEnumerable<string>
, ponieważ łańcuch jest podtypem obiektu. Są one uważane za dwa zupełnie niezwiązane ze sobą typy, takie jak ciąg i int (nadal oba są podtypami obiektu, ale wszystko jest)
Istnieje kilka obejść i wyjątki dla tego problemu, na który natknąłeś się.
Po pierwsze, możesz rzucić każdy ciąg indywidualnie do obiektu, jeśli korzystasz z .NET 3.0, możesz to zrobić przy użyciu metody przedłużania Cast<T>()
. W przeciwnym razie możesz użyć foreach i umieścić wynik w nowej zmiennej typu statycznego.
Po drugie, tablice są wyjątkiem dla typu odniesienia, tj. Przekazanie typu string [] do metody akceptującej obiekty [] powinno działać.
Tylko zaktualizuj tę starszą odpowiedź, kowariancję i contravariance są teraz obsługiwane w C#, a to jest teraz prawidłowe rzutowanie. –
Należy używać
IEnumerable<T>
jeśli chcesz przekazać w różnych typach, potem można wyszukać T, aby dowiedzieć się, jakiego typu jest.
Dobrym sposobem na przemyślenie tego jest zadać sobie pytanie "Co by się stało, gdybyś mógł to zrobić?". Weź następujący przykład:
IEnumerable<String> strings=...;
IEnumerable<Object> objects = strings; // assume this were legal
objects.Add(new Integer(5)); // what the...
Właśnie dodaliśmy liczbę całkowitą do listy ciągów. Kompilator robi to, aby zachować bezpieczeństwo typu.
IEnumerable
Dobrze zamień każdą generyczną klasę, która ma metodę Add, taką jak List, następnie –
-1: 'IEnumerable
Najprostszym sposobem przekazywania IEnumerable<string>
funkcjonować wymaga IEnumerable<object>
jest przez funkcję konwersji w następujący sposób:
public IEnumerable<object> convert<T>(IEnumerable<T> enumerable)
{
foreach (T o in enumerable)
yield return o;
}
gdy C 4 wychodzi, to nie będzie neccessary, ponieważ będzie wspierać kowariancji i kontrawariancji.
To jest dokładnie to, co Cast <> jest dla :) –
Dzięki! Już przegłosowałem odpowiedź na tę sugestię. –
Jak inni podkreślili, typy generyczne są niezmienne. IEnumerable<T>
może być co-wariantem, ale C# obecnie nie obsługuje określania wariantów. C# 4.0 ma obsługiwać warianty, więc może być obsługiwane w przyszłości.
Aby obejść ten problem, można teraz użyć metody LINQ: Cast<object>()
. Zakładając, że masz metodę o nazwie Foo
, która zajmuje IEnumerable<object>>
.Możesz to tak nazwać,
Foo(stringEnumerable.Cast<object>());
Jak to jest niemożliwe? Czy rzuca wyjątek ArgumentException? Poza tym, jaki jest sens używania ogólnego IEnumerable, jeśli chcesz zadeklarować to jako obiekt? –
To jest błąd kompilacji –
To samo, co: http://stackoverflow.com/questions/6557/in-c-why-cant-a-liststring-object-be-stored-in--listobject-variable –