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 X
deriva 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 T
aquí 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 Giraffe
derive de Animal
:
IEnumerable<Animal> animals = giraffes;
Esto se debe a que IEnumerable<T>
admite la covarianza T
: esto tiene sentido dado que IEnumerable
implica que la colección no se puede cambiar, ya que no admite métodos para Agregar o quitar elementos de la colección. Tenga out
en 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 List
no 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 InvalidCastException
si no es posible lanzar uno o más elementos (lo que sería el mismo comportamiento que hacer lo explícito). emitido en el foreach
bucle del OP ).
Filtrado y Fundición con OfType<T>()
Si la lista de entrada contiene elementos de tipos diferentes e incompatibles, InvalidCastException
se 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 InvalidCastException
obtendrá 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