Um zu Swekos Punkt hinzuzufügen:
Der Grund warum die Besetzung
var listOfX = new List<X>();
ListOf<Y> ys = (List<Y>)listOfX; // Compile error: Cannot implicitly cast X to Y
ist nicht möglich, weil das im Typ T invariantList<T>
ist und es daher keine Rolle spielt, ob es von ) stammt - dies liegt daran, dass definiert ist als:X
Y
List<T>
public class List<T> : IList<T>, ICollection<T>, IEnumerable<T> ... // Other interfaces
(Beachten Sie, dass in dieser Deklaration der Typ T
hier keine zusätzlichen Varianzmodifikatoren enthält.)
Wenn jedoch wandelbar Sammlungen sind nicht in Ihrem Design erforderlich, eine upcast auf vielen der unveränderlichen Sammlungen, ist möglich , zum Beispiel vorgesehen , dass Giraffe
ergibt sich aus Animal
:
IEnumerable<Animal> animals = giraffes;
Dies liegt daran, dass IEnumerable<T>
die Kovarianz in unterstützt wird. T
Dies ist sinnvoll, IEnumerable
da die Auflistung nicht geändert werden kann, da Methoden zum Hinzufügen oder Entfernen von Elementen zur Auflistung nicht unterstützt werden. Beachten Sie das out
Schlüsselwort in der Deklaration von IEnumerable<T>
:
public interface IEnumerable<out T> : IEnumerable
( Hier ist eine weitere Erklärung für den Grund, warum veränderbare Sammlungen wie List
nicht unterstützt werden können covariance
, während unveränderliche Iteratoren und Sammlungen dies können.)
Casting mit .Cast<T>()
Wie andere bereits erwähnt haben, .Cast<T>()
kann es auf eine Sammlung angewendet werden, um eine neue Sammlung von Elementen zu projizieren, die in T umgewandelt wurden. Dies führt jedoch zu einem, InvalidCastException
wenn die Umwandlung in ein oder mehrere Elemente nicht möglich ist (was das gleiche Verhalten wie beim expliziten Ausführen wäre in die foreach
Schleife des OP geworfen ).
Filtern und Gießen mit OfType<T>()
Wenn die Eingabeliste Elemente unterschiedlicher, inkompatibler Typen enthält, kann das Potenzial InvalidCastException
durch Verwendung von .OfType<T>()
anstelle von vermieden werden .Cast<T>()
. ( .OfType<>()
Überprüft vor dem Versuch der Konvertierung, ob ein Element in den Zieltyp konvertiert werden kann, und filtert inkompatible Typen heraus.)
für jedes
Beachten Sie auch, dass, wenn das OP dies stattdessen geschrieben hätte: (beachten Sie das expliziteY y
in der foreach
)
List<Y> ListOfY = new List<Y>();
foreach(Y y in ListOfX)
{
ListOfY.Add(y);
}
dass das Casting auch versucht wird. Wenn jedoch keine Besetzung möglich ist, InvalidCastException
ergibt sich ein Wille.
Beispiele
Zum Beispiel angesichts der einfachen (C # 6) Klassenhierarchie:
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) { }
}
Bei der Arbeit mit einer Sammlung gemischter Typen:
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();
Wohingegen:
var castedAnimals = mixedAnimals.OfType<Elephant>()
.ToList();
// Ellie
filtert nur die Elefanten heraus - dh Zebras werden eliminiert.
Betreff: Implizite Cast-Operatoren
Ohne dynamische, benutzerdefinierte Konvertierungsoperatoren werden nur zur Kompilierungszeit * verwendet. Selbst wenn ein Konvertierungsoperator zwischen beispielsweise Zebra und Elephant verfügbar wäre, würde sich das obige Laufzeitverhalten der Konvertierungsansätze nicht ändern.
Wenn wir einen Konvertierungsoperator hinzufügen, um ein Zebra in einen Elefanten zu konvertieren:
public class Zebra : Animal
{
public Zebra(string name) : base(name) { }
public static implicit operator Elephant(Zebra z)
{
return new Elephant(z.Name);
}
}
Stattdessen angesichts der obigen Umwandlungsoperator, wird der Compiler in der Lage sein , den Typ des nachfolgend Array ändern aus Animal[]
zu Elephant[]
bestimmten, dass die Zebras kann nun zu einer homogenen Ansammlung von Elefanten umgewandelt werden:
var compilerInferredAnimals = new []
{
new Zebra("Zed"),
new Elephant("Ellie")
};
Verwenden impliziter Konvertierungsoperatoren zur Laufzeit
* Wie von Eric erwähnt, kann auf den Konvertierungsoperator jedoch zur Laufzeit zugegriffen werden, indem auf Folgendes zurückgegriffen wird 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