Wiem, że możliwe jest rzutowanie listy elementów z jednego typu na inny (biorąc pod uwagę, że twój obiekt ma metodę publicznego jawnego operatora jawnego, aby wykonać rzutowanie) w następujący sposób:
List<Y> ListOfY = new List<Y>();
foreach(X x in ListOfX)
ListOfY.Add((Y)x);
Ale czy nie można rzucić całej listy jednocześnie? Na przykład,
ListOfY = (List<Y>)ListOfX;
c#
list
casting
ienumerable
Jimbo
źródło
źródło
Odpowiedzi:
Jeśli
X
naprawdę można go rzucićY
, powinieneś być w stanie użyćKilka rzeczy, o których należy pamiętać (H / T dla komentujących!)
using System.Linq;
aby uzyskać tę metodę rozszerzeniaList<Y>
zostanie utworzone przez połączenie zToList()
.źródło
Cast<T>
metoda nie obsługuje niestandardowych operatorów konwersji. Dlaczego pomocnik Linq Cast nie działa z niejawnym operatorem rzutowania .Bezpośrednie rzucanie
var ListOfY = (List<Y>)ListOfX
nie jest możliwe, ponieważ wymagałoby ko / kontrawariancji tegoList<T>
typu, a po prostu nie można tego zagwarantować w każdym przypadku. Przeczytaj dalej, aby zobaczyć rozwiązania tego problemu z rzutowaniem.Chociaż normalne wydaje się być pisanie takiego kodu:
ponieważ możemy zagwarantować, że każdy ssak będzie zwierzęciem, jest to oczywiście błąd:
ponieważ nie każde zwierzę jest ssakiem.
Jednak używając C # 3 i nowszych, możesz użyć
co nieco ułatwia casting. Jest to syntaktycznie równoważne z dodawanym kodem jeden po drugim, ponieważ używa jawnego rzutowania do rzutowania każdego
Mammal
z listy naAnimal
i nie powiedzie się, jeśli rzutowanie nie powiedzie się.Jeśli chcesz mieć większą kontrolę nad procesem rzutowania / konwersji, możesz użyć
ConvertAll
metodyList<T>
klasy, która może użyć dostarczonego wyrażenia do konwersji elementów. Ma dodaną korzyść, którą zwracaList
zamiastIEnumerable
, więc nie.ToList()
jest to konieczne.źródło
Aby dodać do punktu Sweko:
Powód, dla którego obsada
nie jest możliwe, ponieważ
List<T>
jest niezmienny w typie T, a zatem nie ma znaczenia, czyX
pochodzi odY
) - dzieje się tak, ponieważList<T>
jest zdefiniowany jako:(Uwaga: w tej deklaracji wpisz
T
tutaj nie ma żadnych dodatkowych modyfikatorów wariancji)Jeśli jednak zmienne kolekcje nie są wymagane w twoim projekcie, możliwy jest upcast do wielu niezmiennych kolekcji , np. Pod warunkiem, że
Giraffe
pochodzi zAnimal
:Jest tak, ponieważ
IEnumerable<T>
obsługuje kowariancję wT
- ma to sens, biorąc pod uwagę, żeIEnumerable
oznacza to, że kolekcji nie można zmienić, ponieważ nie obsługuje ona metod dodawania lub usuwania elementów z kolekcji. Zwróć uwagę naout
słowo kluczowe w deklaracjiIEnumerable<T>
:( Oto dalsze wyjaśnienie powodu, dla którego zmienne kolekcje, takie jak,
List
nie mogą obsługiwaćcovariance
, podczas gdy niezmienne iteratory i kolekcje mogą.)Przesyłam z
.Cast<T>()
Jak wspomnieli inni,
.Cast<T>()
można zastosować do kolekcji, aby rzutować nową kolekcję elementówInvalidCastException
rzutowanych na T, jednak spowoduje to rzutowanie, jeśli rzutowanie na jeden lub więcej elementów nie jest możliwe (co byłoby tym samym zachowaniem, co wykonywanie jawnych obsadzone wforeach
pętli OP ).Filtrowanie i przesyłanie za pomocą
OfType<T>()
Jeśli lista wejściowa zawiera elementy różnych, niekompatybilnych typów, potencjału
InvalidCastException
można uniknąć, używając.OfType<T>()
zamiast.Cast<T>()
. (.OfType<>()
sprawdza, czy element można przekonwertować na typ docelowy, przed próbą konwersji i odfiltrowuje typy niezgodne).dla każdego
Należy również pamiętać, że jeśli PO zamiast tego napisał: (należy zwrócić uwagę na wyraźne
Y y
wforeach
)że próba odlewania również zostanie podjęta. Jeśli jednak rzut nie jest możliwy,
InvalidCastException
nastąpi wynik.Przykłady
Na przykład, biorąc pod uwagę prostą hierarchię klas (C # 6):
Podczas pracy z kolekcją typów mieszanych:
Natomiast:
odfiltrowuje tylko słonie - tzn. zebry są eliminowane.
Re: Domniemani operatorzy rzutowania
Bez dynamiki operatory konwersji zdefiniowane przez użytkownika są używane tylko w czasie kompilacji *, więc nawet jeśli operator konwersji między powiedzmy, że Zebra i Elephant został udostępniony, powyższe zachowanie podejścia do konwersji nie zmieni się.
Jeśli dodamy operator konwersji, aby przekonwertować Zebrę na słonia:
Zamiast tego, biorąc pod uwagę powyższy operator konwersji, kompilator będzie mógł zmienić typ poniższej tablicy z
Animal[]
naElephant[]
, biorąc pod uwagę, że Zebry można teraz przekształcić w jednorodną kolekcję słoni:Korzystanie z niejawnych operatorów konwersji w czasie wykonywania
* Jak wspomniał Eric, do operatora konwersji można jednak uzyskać dostęp w czasie wykonywania, korzystając z
dynamic
:źródło
foreach
nie filtruje, ale użycie bardziej pochodnego typu jako zmiennej iteracyjnej zmusi kompilator do próby rzutowania, co zakończy się niepowodzeniem w przypadku pierwszego elementu, który nie jest zgodny.Możesz użyć
List<Y>.ConvertAll<T>([Converter from Y to T]);
źródło
Nie jest to do końca odpowiedź na to pytanie, ale może być przydatna dla niektórych: jak powiedział @SWeko, dzięki kowariancji i kontrawariancji
List<X>
nie można w nią wrzucićList<Y>
, aleList<X>
można ją wrzucićIEnumerable<Y>
, a nawet z ukrytą obsadą.Przykład:
ale
Dużą zaletą jest to, że nie tworzy nowej listy w pamięci.
źródło
źródło