Cast List <T> a List <Interface>

110
public interface IDic
{
    int Id { get; set; }
    string Name { get; set; }
}
public class Client : IDic
{

}

¿Cómo puedo enviar List<Client>a List<IDic>?

Andrew Kalashnikov
fuente

Respuestas:

250

No puede transmitirlo (conservando la identidad de referencia), eso no sería seguro. Por ejemplo:

public interface IFruit {}

public class Apple : IFruit {}
public class Banana : IFruit {}

...

List<Apple> apples = new List<Apple>();
List<IFruit> fruit = apples; // Fortunately not allowed
fruit.Add(new Banana());

// Eek - it's a banana!
Apple apple = apples[0];

Ahora puede convertir List<Apple>a IEnumerable<IFruit>en .NET 4 / C # 4 debido a la covarianza, pero si desea un List<IFruit>, tendrá que crear una nueva lista. Por ejemplo:

// In .NET 4, using the covariance of IEnumerable<T>
List<IFruit> fruit = apples.ToList<IFruit>();

// In .NET 3.5
List<IFruit> fruit = apples.Cast<IFruit>().ToList();

Pero esto no es lo mismo que lanzar la lista original, porque ahora hay dos listas separadas . Esto es seguro, pero debe comprender que los cambios realizados en una lista no se verán en la otra. (Por supuesto, se verán las modificaciones a los objetos a los que se refieren las listas).

Jon Skeet
fuente
6
¡como si 'todavía estuvieran en el edificio'!
Andras Zoltan
1
¿Cuál es la diferencia entre hacer la covarianza tolist y hacer una nueva List <IFruit> (); luego un foreach sobre la lista original y agregando cada elemento a la lista IFruit? En el primer caso ... la referencia del objeto sería la misma, ¿correcto? Entonces ... si eso es cierto, para mí personalmente tiene poco sentido que no se pueda lanzar directamente toda la lista. ?
Robert Noack
3
@RobertNoack: ¿Qué quieres decir con "la referencia del objeto"? La referencia de objeto de cada elemento es la misma, pero no es lo mismo que convertir la referencia de lista en sí. Suponga que puede lanzar la referencia, de modo que tenga una referencia de tipo en tiempo de compilación List<IFruit>que en realidad sea una referencia a un List<Apple>. ¿Qué esperaría que sucediera si agregara una Bananareferencia a eso List<IFruit>?
Jon Skeet
1
Oh. Creo que necesito aprender a leer. Pensé que la respuesta original decía que los objetos de la lista serían copiados y recreados en profundidad, lo que no tenía sentido para mí. Pero claramente me perdí la parte en la que solo se crea una nueva lista con los mismos objetos.
Robert Noack
2
@ TrươngQuốcKhánh: No tengo idea de lo que cualquiera de sus miradas como código, o si usted tiene una usingdirectiva para System.Linq, o lo que usted está tratando de llamarlo en adelante, no estoy seguro de cómo se espera que sea capaz de ayuda. Le sugiero que investigue más y, si todavía está atascado, haga una pregunta con un ejemplo mínimo reproducible .
Jon Skeet
6

Un iterador Cast y .ToList ():

List<IDic> casted = input.Cast<IDic>().ToList() hará el truco.

Al principio dije que la covarianza funcionaría, pero como Jon ha señalado correctamente; ¡No, no lo hará!

Y originalmente también dejé estúpidamente la ToList()llamada

Andras Zoltan
fuente
Castdevuelve un IEnumerable<T>, no un List<T>- y no, la covarianza no permitirá esta conversión, porque no sería seguro - vea mi respuesta.
Jon Skeet
Desde la página a la que vinculó: "Solo los tipos de interfaz y los tipos de delegado pueden tener parámetros de tipo de variante"
Jon Skeet
@Jon: me di cuenta de que ToList()faltaba el antes de leer tu comentario; pero sí, como ha demostrado, ¡la covarianza no funcionará! Doh!
Andras Zoltan
Correcto. La covarianza aún puede ayudar, ya que significa que no necesita la Castllamada en .NET 4, siempre que especifique el argumento de tipo a ToList.
Jon Skeet
5

Yo también tuve este problema y, después de leer la respuesta de Jon Skeet, modifiqué mi código de uso List<T>a uso IEnumerable<T>. Aunque esto no responde a la pregunta original de la OP de ¿Cómo puedo enviar contenido List<Client>aList<IDic> , no evita la necesidad de hacerlo y por lo tanto puede ser de utilidad para otros que se encuentran con este problema. Por supuesto, esto supone que el código que requiere el uso List<IDic>está bajo su control.

P.ej:

public void ProcessIDic(IEnumerable<IDic> sequence)
{
   // Implementation
}

En vez de:

public void ProcessIDic(List<IDic> list)
{
   // Implementation
}
Ɖiamond ǤeezeƦ
fuente
3

Si puede usar LINQ, puede hacer esto ...

List<Client> clientList = new List<Client>();
List<IDic> list = clientList.Select(c => (IDic)c).ToList();
musefan
fuente
3
List<Client> listOfA = new List<Client>();
List<IDic> list = listOfA.Cast<IDic>().ToList();
Marc Messing
fuente
0

Solo es posible creando nuevos List<IDic>y transfiriendo todos los elementos.

Piotr Auguscik
fuente
¿Algún comentario por qué votó en contra, ya que el significado general es el mismo que el de todas las demás respuestas?
Piotr Auguscik
Supongo que te votaron en contra porque dijiste que solo era posible crear una nueva lista, pero otros han publicado lo contrario ... aunque no mi voto en contra)
musefan
Bueno, crean nuevas listas pero no con un nuevo operador, lo que no cambia el hecho de que lo hacen.
Piotr Auguscik
0

En .Net 3.5, puede hacer lo siguiente:

List<ISomeInterface> interfaceList = new List<ISomeInterface>(list.Cast<ISomeInterface>());

El constructor de List en este caso toma un IEnumerable. aunque la
lista solo es convertible a IEnumerable. Aunque myObj puede convertirse en ISomeInterface, el tipo IEnumerable no se puede convertir en IEnumerable.

Kent Aguilar
fuente
El problema es que esto hace una copia de la lista, la mayoría de las veces desea realizar operaciones en la lista original
lanza el