convert cast c# list casting ienumerable

c# - cast - ¿Sintaxis más corta para lanzar desde una Lista<X> a una Lista<Y>?



ienumerable c# (4)

El lanzamiento directo var ListOfY = (List<Y>)ListOfX no es posible porque requeriría co/contravariance del tipo List<T> , y eso simplemente no se puede garantizar en todos los casos. Por favor, sigue leyendo para ver las soluciones a este problema de fundición.

Si bien parece normal poder escribir código como este:

List<Animal> animals = (List<Animal>) mammalList;

porque podemos garantizar que cada mamífero será un animal, obviamente esto es un error:

List<Mammal> mammals = (List<Mammal>) animalList;

ya que no todos los animales son mamíferos.

Sin embargo, usando C # 3 y superior, puede usar

IEnumerable<Animal> animals = mammalList.Cast<Animal>();

eso facilita el casting un poco. Esto es sintácticamente equivalente a su código de adición uno por uno, ya que usa un lanzamiento explícito para convertir cada Mammal en la lista a un Animal , y fallará si el lanzamiento no es exitoso.

Si desea tener más control sobre el proceso de conversión / conversión, puede usar el método ConvertAll de la clase List<T> , que puede usar una expresión proporcionada para convertir los elementos. Tiene la ventaja adicional de que devuelve una List , en lugar de IEnumerable , por lo que no es necesario .ToList() .

List<object> o = new List<object>(); o.Add("one"); o.Add("two"); o.Add(3); IEnumerable<string> s1 = o.Cast<string>(); //fails on the 3rd item List<string> s2 = o.ConvertAll(x => x.ToString()); //succeeds

Sé que es posible emitir una lista de elementos de un tipo a otro (dado que su objeto tiene un método de operador explícito estático público para hacer el casting) uno a la vez de la siguiente manera:

List<Y> ListOfY = new List<Y>(); foreach(X x in ListOfX) ListOfY.Add((Y)x);

¿Pero no es posible emitir toda la lista al mismo tiempo? Por ejemplo,

ListOfY = (List<Y>)ListOfX;


Puede usar List.ConvertAll ([Converter from Y to T]);


Si X realmente se puede convertir a Y , deberías poder usar

List<Y> listOfY = listOfX.Cast<Y>().ToList();

Algunas cosas que debe tener en cuenta (¡H / T para los comentaristas!)


Para agregar al punto de Sweko:

La razón por la cual el reparto

var listOfX = new List<X>(); ListOf<Y> ys = (List<Y>)listOfX; // Compile error: Cannot implicitly cast X to Y

no es posible es porque la List<T> es invariante en el Tipo T y, por lo tanto, no importa si X deriva de Y ), esto es porque la 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 un upcast en muchas de las colecciones inmutables, por ejemplo, siempre que Giraffe derive de Animal :

IEnumerable<Animal> animals = giraffes;

Esto se debe a que IEnumerable<T> admite covarianza en 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 Eliminar elementos de la colección. Tenga en cuenta la palabra clave out en la declaración de IEnumerable<T> :

public interface IEnumerable<out T> : IEnumerable

( Aquí hay más explicaciones sobre la razón por la cual las colecciones mutables como List no pueden soportar la covariance , mientras que las iteraciones y colecciones inmutables sí pueden).

Casting con .Cast<T>()

Como otros han mencionado, .Cast<T>() se puede aplicar a una colección para proyectar una nueva colección de elementos convertidos a T, sin embargo, esto arrojará una InvalidCastException si el lanzamiento en uno o más elementos no es posible (lo cual ser el mismo comportamiento que hacer el lanzamiento explícito en el bucle foreach del OP).

Filtrado y lanzamiento con OfType<T>()

Si la lista de entrada contiene elementos de diferentes tipos incompatibles, la posible InvalidCastException se puede evitar utilizando .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).

Uso de foreach () para el filtrado de tipo

También tenga en cuenta que si el OP hubiera escrito esto en su lugar: (tenga en cuenta el Y y explícito en el foreach )

List<Y> ListOfY = new List<Y>(); foreach(Y y in ListOfX) { ListOfY.Add(y); }

que cualquier elemento que no sea Y o que no pueda convertirse en Y se omitirá y eliminará de la lista resultante. es decir, foreach(Y y in ListOfX){ ... Add(y) } es equivalente a ListOfX.OfType<Y>()

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) { } }

Cuando se trabaja 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:

foreach(Elephant animal in mixedAnimals) { castedAnimals.Add(animal); } // Ellie

y

var castedAnimals = mixedAnimals.OfType<Elephant>() .ToList(); // Ellie

Ambos enfoques filtran solo a los Elefantes, es decir, las cebras se eliminan.

Re: operadores implícitos de molde

Sin dinámica, los operadores de conversión definidos por el usuario solo se utilizan en compile-time *, por lo que incluso si un operador de conversión entre 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 un Zebra 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 siguiente 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 menciona Eric, sin embargo 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