Cast List <T> a List <Interface>


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

}

Come posso trasmettere List<Client>a List<IDic>?

Risposte:


250

Non puoi lanciarlo (preservando l'identità di riferimento) - non sarebbe sicuro. Per esempio:

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];

Ora puoi convertire a List<Apple>in un IEnumerable<IFruit>in .NET 4 / C # 4 a causa della covarianza, ma se vuoi un List<IFruit>dovresti creare un nuovo elenco. Per esempio:

// 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();

Ma questo non è lo stesso che lanciare l'elenco originale, perché ora ci sono due elenchi separati . Questo è sicuro, ma è necessario comprendere che le modifiche apportate a un elenco non verranno visualizzate nell'altro elenco. ( Ovviamente si vedranno le modifiche agli oggetti a cui fanno riferimento gli elenchi.)


6
come se fossero "ancora nell'edificio"!
Andras Zoltan

1
Qual è la differenza tra fare la covarianza tolist e fare new List <IFruit> (); quindi una foreach sulla lista originale e l'aggiunta di ogni elemento alla lista IFruit? Ad ogni modo ... il riferimento all'oggetto sarebbe lo stesso, giusto? Quindi ... se questo è vero, non ha molto senso per me personalmente che non puoi semplicemente lanciare direttamente l'intera lista. ?
Robert Noack

3
@RobertNoack: Cosa intendi per "riferimento all'oggetto"? Il riferimento all'oggetto di ogni elemento è lo stesso, ma non è lo stesso del cast del riferimento all'elenco stesso. Supponiamo di poter eseguire il cast del riferimento, in modo da avere un riferimento di tipo in fase di compilazione List<IFruit>che in realtà era un riferimento a un file List<Apple>. Cosa ti aspetteresti che succeda se aggiungessi un Bananariferimento a questo List<IFruit>?
Jon Skeet

1
Oh. Penso di aver bisogno di imparare a leggere. Pensavo che la risposta originale dicesse che gli oggetti nell'elenco sarebbero stati copiati e ricreati in profondità, il che non aveva senso per me. Ma ho chiaramente perso la parte in cui viene creato solo un nuovo elenco con gli stessi oggetti.
Robert Noack

2
@ TrươngQuốcKhánh: Non ho idea di come sia il tuo codice, o se hai una usingdirettiva per System.Linq, o come stai cercando di chiamarlo, non sono sicuro di come ti aspetti che io sia in grado di aiutarti. Ti suggerisco di fare più ricerche e, se sei ancora bloccato, fai una domanda con un esempio riproducibile minimo .
Jon Skeet

6

Un iteratore Cast e .ToList ():

List<IDic> casted = input.Cast<IDic>().ToList() farà il trucco.

Inizialmente ho detto che la covarianza avrebbe funzionato, ma come ha giustamente sottolineato Jon; no non lo farà!

E originariamente ho anche interrotto stupidamente la ToList()chiamata


Castrestituisce an IEnumerable<T>, non a List<T>- e no, la covarianza non consentirà questa conversione, perché non sarebbe sicura - vedi la mia risposta.
Jon Skeet

Dalla pagina a cui hai collegato: "Solo i tipi di interfaccia e i tipi di delegati possono avere parametri di tipo variante"
Jon Skeet

@ Jon - avevo capito che ToList()mancava prima di leggere il tuo commento; ma sì, come hai dimostrato, ovviamente la covarianza non funzionerà! Doh!
Andras Zoltan

Destra. La covarianza può ancora aiutare, poiché significa che non hai bisogno diCast chiamata in .NET 4, purché si specifichi l'argomento di tipo a ToList.
Jon Skeet

5

Anch'io ho avuto questo problema e dopo aver letto la risposta di Jon Skeet ho modificato il mio codice dall'uso List<T>all'uso IEnumerable<T>. Anche se questo non rispondere alla domanda originale del PO di Come posso lanciare List<Client>aList<IDic> , lo fa evitare la necessità di farlo e quindi potrebbe essere utile ad altri che si verifichi questo problema. Questo ovviamente presuppone che il codice che richiede l'uso diList<IDic> sia sotto il tuo controllo.

Per esempio:

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

Invece di:

public void ProcessIDic(List<IDic> list)
{
   // Implementation
}

3

Se puoi usare LINQ, puoi farlo ...

List<Client> clientList = new List<Client>();
List<IDic> list = clientList.Select(c => (IDic)c).ToList();

3
List<Client> listOfA = new List<Client>();
List<IDic> list = listOfA.Cast<IDic>().ToList();

0

È possibile solo creando nuovi List<IDic>e trasferendo tutti gli elementi.


Qualche commento perché downvoted, dal momento che il significato generale è lo stesso di tutte le altre risposte?
Piotr Auguscik

La mia ipotesi sarebbe che tu abbia ricevuto un voto negativo perché hai detto che era possibile solo creare un nuovo elenco, ma altri hanno pubblicato il contrario ... non il mio
voto negativo

Beh, creano nuove liste ma non con un nuovo operatore, il che non cambia il fatto che lo facciano.
Piotr Auguscik

0

In .Net 3.5, puoi fare quanto segue:

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

Il costruttore di List in questo caso accetta un IEnumerable.
list è però convertibile solo in IEnumerable. Anche se myObj può essere convertibile in ISomeInterface, il tipo IEnumerable non è convertibile in IEnumerable.


Il problema è che questo fa una copia dell'elenco, la maggior parte delle volte si desidera eseguire operazioni sull'elenco originale
rotoli
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.