Cast List <T> to List <Interface>

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

}

Wie kann ich werfen List<Client>zu List<IDic>?

Andrew Kalashnikov
quelle

Antworten:

249

Sie können es nicht umsetzen (Referenzidentität bewahren) - das wäre unsicher. Beispielsweise:

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

Jetzt können Sie aufgrund von Kovarianz eine List<Apple>in eine IEnumerable<IFruit>in .NET 4 / C # 4 konvertieren. Wenn Sie jedoch eine möchten, müssen List<IFruit>Sie eine neue Liste erstellen . Beispielsweise:

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

Dies ist jedoch nicht dasselbe wie das Casting der ursprünglichen Liste - da es jetzt zwei separate Listen gibt. Dies ist sicher, aber Sie müssen verstehen, dass Änderungen an einer Liste nicht in der anderen Liste angezeigt werden. (Änderungen an den Objekten , auf die sich die Listen beziehen, werden natürlich angezeigt.)

Jon Skeet
quelle
6
als wären sie 'noch im Gebäude'!
Andras Zoltan
1
Was ist der Unterschied zwischen der Kovarianz-Tolist und der neuen Liste <IFruit> ()? dann ein foreach über die ursprüngliche Liste und das Hinzufügen jedes Elements zur IFruit-Liste? In jeder Hinsicht ... wäre die Objektreferenz dieselbe, richtig? Also ... wenn das stimmt, macht es für mich persönlich wenig Sinn, dass man nicht einfach die ganze Liste direkt besetzen kann. ?
Robert Noack
3
@ RobertNoack: Was meinst du mit "der Objektreferenz"? Die Objektreferenz jedes Elements ist dieselbe, aber das ist nicht dasselbe wie das Umwandeln der Listenreferenz selbst. Angenommen, Sie könnten die Referenz umwandeln, sodass Sie eine Referenz vom Typ der Kompilierungszeit hatten, List<IFruit>die tatsächlich eine Referenz auf a war List<Apple>. Was würden Sie erwarten, wenn Sie einen BananaVerweis darauf hinzufügen würden List<IFruit>?
Jon Skeet
1
Oh. Ich denke, ich muss lesen lernen. Ich dachte, die ursprüngliche Antwort besagte, dass die Objekte in der Liste tief kopiert und neu erstellt würden, was für mich keinen Sinn ergab. Aber ich habe den Teil, in dem nur eine neue Liste mit denselben Objekten erstellt wird, deutlich übersehen.
Robert Noack
2
@ TrươngQuốcKhánh: Ich habe keine Ahnung , was jeder von Code wie folgt aussieht, oder ob Sie eine haben usingRichtlinie für System.Linq, oder was Sie versuchen , es zu nennen, ich bin sicher , dass nicht , wie du mich erwarten zu Hilfe zu können. Ich schlage vor, Sie recherchieren mehr und wenn Sie immer noch nicht weiterkommen, stellen Sie eine Frage mit einem minimal reproduzierbaren Beispiel .
Jon Skeet
6

Ein Cast-Iterator und .ToList ():

List<IDic> casted = input.Cast<IDic>().ToList() wird den Trick machen.

Ursprünglich sagte ich, Kovarianz würde funktionieren - aber wie Jon zu Recht betont hat; nein wird es nicht!

Und ursprünglich habe ich den ToList()Anruf auch dumm abgebrochen

Andras Zoltan
quelle
Casteine Rendite IEnumerable<T>, keine List<T>- und nein, Kovarianz nicht um diese Umwandlung zu ermöglichen, da es unsicher wäre - meine Antwort sehen.
Jon Skeet
Von der Seite, auf die Sie verlinkt haben: "Nur Schnittstellentypen und Delegatentypen können Variantentypparameter haben"
Jon Skeet
@ Jon - Ich hatte festgestellt, dass das ToList()fehlte, bevor ich Ihren Kommentar gelesen habe. Aber ja, wie Sie gezeigt haben, funktioniert Covariance natürlich nicht! Doh!
Andras Zoltan
Richtig. Kovarianz kann weiterhin hilfreich sein, da Sie den CastAufruf in .NET 4 nicht benötigen , solange Sie das Typargument angeben ToList.
Jon Skeet
5

Auch ich hatte dieses Problem und nachdem ich Jon Skeets Antwort gelesen hatte, änderte ich meinen Code von der Verwendung List<T>zur Verwendung IEnumerable<T>. Obwohl dies nicht die OP ursprüngliche Frage beantwortet Wie kann ich werfen List<Client>zuList<IDic> , ist es die Notwendigkeit zu vermeiden , dies zu tun, und kann somit für andere hilfreich sein , die dieses Problem auftreten. Dies setzt natürlich voraus, dass der Code, für den die Verwendung erforderlich List<IDic>ist, unter Ihrer Kontrolle steht.

Z.B:

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

Anstatt:

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

Wenn Sie LINQ verwenden können, können Sie dies tun ...

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

Es ist nur möglich, indem Sie neue List<IDic>Elemente erstellen und alle Elemente übertragen.

Piotr Auguscik
quelle
Irgendein Kommentar, warum herabgestimmt, da die allgemeine Bedeutung dieselbe ist wie alle anderen Antworten?
Piotr Auguscik
Meine Vermutung wäre, dass Sie abgelehnt wurden, weil Sie sagten, es sei nur möglich, eine neue Liste zu erstellen, aber andere haben das Gegenteil gepostet ... aber nicht meine Ablehnung)
Musefan
Nun, sie erstellen neue Listen, aber nicht mit einem neuen Operator, was die Tatsache, dass sie dies tun, nicht ändert.
Piotr Auguscik
0

In .Net 3.5 können Sie Folgendes tun:

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

Der Konstruktor für List verwendet in diesem Fall eine IEnumerable.
Liste ist jedoch nur in IEnumerable konvertierbar. Obwohl myObj möglicherweise in ISomeInterface konvertierbar ist, kann der Typ IEnumerable nicht in IEnumerable konvertiert werden.

Kent Aguilar
quelle
Das Problem ist, dass dies eine Kopie der Liste erstellt. Meistens möchten Sie Operationen an der ursprünglichen Liste ausführen
rollt am