Para agregar al punto de Sweko:
La razón por la cual el elenco
var listOfX = new List<X>();
ListOf<Y> ys = (List<Y>)listOfX; // Compile error: Cannot implicitly cast X to Y
no es posible porque List<T>es invariante en el Tipo T y, por lo tanto, no importa si se Xderiva de Y), esto se debe a que List<T>se define como:
public class List<T> : IList<T>, ICollection<T>, IEnumerable<T> ... // Other interfaces
(Tenga en cuenta que en esta declaración, escriba Taquí no tiene modificadores de varianza adicionales)
Sin embargo, si no se requieren colecciones mutables en su diseño, es posible una transmisión ascendente en muchas de las colecciones inmutables , por ejemplo, siempre que se Giraffederive de Animal:
IEnumerable<Animal> animals = giraffes;
Esto se debe a que IEnumerable<T>admite la covarianza T: esto tiene sentido dado que IEnumerableimplica que la colección no se puede cambiar, ya que no admite métodos para Agregar o quitar elementos de la colección. Tenga outen cuenta la palabra clave en la declaración de IEnumerable<T>:
public interface IEnumerable<out T> : IEnumerable
( Aquí hay una explicación más detallada de la razón por la cual las colecciones mutables como Listno pueden soportar covariance, mientras que los iteradores y colecciones inmutables sí pueden).
Fundición con .Cast<T>()
Como otros han mencionado, .Cast<T>()se puede aplicar a una colección para proyectar una nueva colección de elementos lanzados a T, sin embargo, hacerlo arrojará un resultado InvalidCastExceptionsi no es posible lanzar uno o más elementos (lo que sería el mismo comportamiento que hacer lo explícito). emitido en el foreachbucle del OP ).
Filtrado y Fundición con OfType<T>()
Si la lista de entrada contiene elementos de tipos diferentes e incompatibles, InvalidCastExceptionse puede evitar el potencial utilizando en .OfType<T>()lugar de .Cast<T>(). ( .OfType<>()comprueba si un elemento se puede convertir al tipo de destino, antes de intentar la conversión, y filtra los tipos incompatibles).
para cada
También tenga en cuenta que si el OP había escrito esto en su lugar: (tenga en cuenta lo explícitoY y en el foreach)
List<Y> ListOfY = new List<Y>();
foreach(Y y in ListOfX)
{
ListOfY.Add(y);
}
que también se intentará el casting. Sin embargo, si no es posible lanzar, se InvalidCastExceptionobtendrá un resultado.
Ejemplos
Por ejemplo, dada la jerarquía de clases 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) { }
}
Al trabajar con una colección de tipos mixtos:
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();
Mientras:
var castedAnimals = mixedAnimals.OfType<Elephant>()
.ToList();
// Ellie
filtra solo los elefantes, es decir, se eliminan las cebras.
Re: operadores de reparto implícito
Sin dinámica, los operadores de conversión definidos por el usuario solo se usan en tiempo de compilación *, por lo que incluso si un operador de conversión entre, por ejemplo, Zebra y Elephant estuviera disponible, el comportamiento del tiempo de ejecución anterior de los enfoques de conversión no cambiaría.
Si agregamos un operador de conversión para convertir una cebra en un elefante:
public class Zebra : Animal
{
public Zebra(string name) : base(name) { }
public static implicit operator Elephant(Zebra z)
{
return new Elephant(z.Name);
}
}
En cambio, dado el operador de conversión anterior, el compilador podrá cambiar el tipo de la matriz a continuación de Animal[]a Elephant[], dado que las cebras ahora se pueden convertir en una colección homogénea de elefantes:
var compilerInferredAnimals = new []
{
new Zebra("Zed"),
new Elephant("Ellie")
};
Uso de operadores de conversión implícita en tiempo de ejecución
* Como mencionó Eric, se puede acceder al operador de conversión en tiempo de ejecución recurriendo a 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