Aby dodać do punktu Sweko:
Powód, dla którego obsada
var listOfX = new List<X>();
ListOf<Y> ys = (List<Y>)listOfX; // Compile error: Cannot implicitly cast X to Y
nie jest możliwe, ponieważ List<T>
jest niezmienny w typie T, a zatem nie ma znaczenia, czy X
pochodzi od Y
) - dzieje się tak, ponieważ List<T>
jest zdefiniowany jako:
public class List<T> : IList<T>, ICollection<T>, IEnumerable<T> ... // Other interfaces
(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 z Animal
:
IEnumerable<Animal> animals = giraffes;
Jest tak, ponieważ IEnumerable<T>
obsługuje kowariancję w T
- ma to sens, biorąc pod uwagę, że IEnumerable
oznacza to, że kolekcji nie można zmienić, ponieważ nie obsługuje ona metod dodawania lub usuwania elementów z kolekcji. Zwróć uwagę na out
słowo kluczowe w deklaracji IEnumerable<T>
:
public interface IEnumerable<out T> : IEnumerable
( 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ów InvalidCastException
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 w foreach
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źneY y
w foreach
)
List<Y> ListOfY = new List<Y>();
foreach(Y y in ListOfX)
{
ListOfY.Add(y);
}
ż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):
public abstract class Animal
{
public string Name { get; }
protected Animal(string name) { Name = name; }
}
public class Elephant : Animal
{
public Elephant(string name) : base(name){}
}
public class Zebra : Animal
{
public Zebra(string name) : base(name) { }
}
Podczas pracy z kolekcją typów mieszanych:
var mixedAnimals = new Animal[]
{
new Zebra("Zed"),
new Elephant("Ellie")
};
foreach(Animal animal in mixedAnimals)
{
// Fails for Zed - `InvalidCastException - cannot cast from Zebra to Elephant`
castedAnimals.Add((Elephant)animal);
}
var castedAnimals = mixedAnimals.Cast<Elephant>()
// Also fails for Zed with `InvalidCastException
.ToList();
Natomiast:
var castedAnimals = mixedAnimals.OfType<Elephant>()
.ToList();
// Ellie
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:
public class Zebra : Animal
{
public Zebra(string name) : base(name) { }
public static implicit operator Elephant(Zebra z)
{
return new Elephant(z.Name);
}
}
Zamiast tego, biorąc pod uwagę powyższy operator konwersji, kompilator będzie mógł zmienić typ poniższej tablicy z Animal[]
na Elephant[]
, biorąc pod uwagę, że Zebry można teraz przekształcić w jednorodną kolekcję słoni:
var compilerInferredAnimals = new []
{
new Zebra("Zed"),
new Elephant("Ellie")
};
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
:
var mixedAnimals = new Animal[] // i.e. Polymorphic collection
{
new Zebra("Zed"),
new Elephant("Ellie")
};
foreach (dynamic animal in mixedAnimals)
{
castedAnimals.Add(animal);
}
// Returns Zed, Ellie