Pour ajouter au point de Sweko:
La raison pour laquelle le casting
var listOfX = new List<X>();
ListOf<Y> ys = (List<Y>)listOfX; // Compile error: Cannot implicitly cast X to Y
n'est pas possible parce que le List<T>
est invariant dans le Type T et donc peu importe qu'il X
dérive de Y
) - c'est parce que List<T>
est défini comme:
public class List<T> : IList<T>, ICollection<T>, IEnumerable<T> ... // Other interfaces
(Notez que dans cette déclaration, le type T
ici n'a pas de modificateurs de variance supplémentaires)
Cependant, si des collections modifiables ne sont pas requises dans votre conception, une conversion ascendante sur un grand nombre des collections immuables est possible , par exemple à condition qu'elles Giraffe
dérivent de Animal
:
IEnumerable<Animal> animals = giraffes;
En effet, IEnumerable<T>
prend en charge la covariance dans T
- cela a du sens étant donné qu'il IEnumerable
implique que la collection ne peut pas être modifiée, car elle ne prend pas en charge les méthodes pour ajouter ou supprimer des éléments de la collection. Notez le out
mot - clé dans la déclaration de IEnumerable<T>
:
public interface IEnumerable<out T> : IEnumerable
( Voici une explication supplémentaire de la raison pour laquelle les collections mutables comme List
ne peuvent pas prendre en charge covariance
, alors que les itérateurs et les collections immuables le peuvent.)
Casting avec .Cast<T>()
Comme d'autres l'ont mentionné, .Cast<T>()
peut être appliqué à une collection pour projeter une nouvelle collection d'éléments castés en T, mais cela provoquera un jet InvalidCastException
si la cast sur un ou plusieurs éléments n'est pas possible (ce qui serait le même comportement que de faire l'explicite dans la foreach
boucle de l'OP ).
Filtrage et diffusion avec OfType<T>()
Si la liste d'entrée contient des éléments de types différents et incompatibles, le potentiel InvalidCastException
peut être évité en utilisant à la .OfType<T>()
place de .Cast<T>()
. ( .OfType<>()
vérifie si un élément peut être converti en type cible, avant de tenter la conversion, et filtre les types incompatibles.)
pour chaque
Notez également que si l'OP l'a écrit à la place: (notez l' expliciteY y
dans le foreach
)
List<Y> ListOfY = new List<Y>();
foreach(Y y in ListOfX)
{
ListOfY.Add(y);
}
que le casting sera également tenté. Cependant, si aucun lancer n'est possible, un InvalidCastException
résultat sera obtenu.
Exemples
Par exemple, étant donné la hiérarchie de classe simple (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) { }
}
Lorsque vous travaillez avec une collection de types mixtes:
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();
Tandis que:
var castedAnimals = mixedAnimals.OfType<Elephant>()
.ToList();
// Ellie
filtre uniquement les éléphants - c'est-à-dire que les zèbres sont éliminés.
Re: opérateurs de distribution implicites
Sans opérateurs de conversion dynamiques définis par l'utilisateur, ils ne sont utilisés qu'au moment de la compilation *, donc même si un opérateur de conversion entre Zebra et Elephant, par exemple, était disponible, le comportement d'exécution des approches de conversion ci-dessus ne changerait pas.
Si nous ajoutons un opérateur de conversion pour convertir un zèbre en éléphant:
public class Zebra : Animal
{
public Zebra(string name) : base(name) { }
public static implicit operator Elephant(Zebra z)
{
return new Elephant(z.Name);
}
}
Au lieu de cela, étant donné l'opérateur de conversion ci-dessus, le compilateur pourra changer le type du tableau ci-dessous de Animal[]
à Elephant[]
, étant donné que les zèbres peuvent maintenant être convertis en une collection homogène d'éléphants:
var compilerInferredAnimals = new []
{
new Zebra("Zed"),
new Elephant("Ellie")
};
Utilisation d'opérateurs de conversion implicite lors de l'exécution
* Comme mentionné par Eric, l'opérateur de conversion est cependant accessible au moment de l'exécution en recourant à 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