Kürzere Syntax zum Umwandeln von einer Liste in eine Liste ?

Ich weiß, dass es möglich ist, eine Liste von Elementen von einem Typ in einen anderen zu schreiben (vorausgesetzt, dass Ihr Objekt eine öffentliche statische explizite Operatormethode hat, um das Casting durchzuführen) einzeln nacheinander:

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

Aber ist es nicht möglich, die gesamte Liste gleichzeitig zu übertragen? Beispielsweise,

 ListOfY = (List)ListOfX; 

   

Wenn X wirklich in Y kann, sollten Sie in der Lage sein zu verwenden

 List listOfY = listOfX.Cast().ToList(); 

Einige Dinge zu beachten (H / T zu Kommentatoren!)

Die direkte var ListOfY = (List)ListOfX ist nicht möglich, da sie eine var ListOfY = (List)ListOfX vom Typ List erfordern würde, und das kann nicht in jedem Fall garantiert werden. Bitte lesen Sie weiter, um die Lösungen für dieses Problem zu sehen.

Während es normal zu sein scheint, Code wie folgt zu schreiben:

 List animals = (List) mammalList; 

weil wir garantieren können, dass jedes Säugetier ein Tier sein wird, ist dies offensichtlich ein Fehler:

 List mammals = (List) animalList; 

denn nicht jedes Tier ist ein Säugetier.

Verwenden Sie jedoch C # 3 und höher

 IEnumerable animals = mammalList.Cast(); 

das erleichtert das Casting ein wenig. Dies entspricht syntaktisch dem Hinzufügen von Code nach dem anderen, da es einen expliziten Cast verwendet, um jedes Mammal in der Liste in ein Animal zu Animal , und es wird fehlschlagen, wenn der Cast nicht erfolgreich ist.

Wenn Sie mehr Kontrolle über den Casting- / Konvertierungsprozess haben ConvertAll , können Sie die ConvertAll Methode der List -class verwenden, die einen bereitgestellten Ausdruck zum Konvertieren der Elemente verwenden kann. Es hat den zusätzlichen Vorteil, dass es eine List anstelle von IEnumerable , also ist keine .ToList() notwendig.

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

Um zu dem Punkt von Sweko hinzuzufügen:

Der Grund, warum die Besetzung

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

ist nicht möglich, weil die List im Typ T invariant ist und es somit keine Rolle spielt, ob X von Y – dies liegt daran, dass List folgt definiert ist:

 public class List : IList, ICollection, IEnumerable ... // Other interfaces 

(Beachten Sie, dass in dieser Deklaration T hier keine zusätzlichen Varianzmodifikatoren enthält).

Wenn jedoch veränderbare Sammlungen in Ihrem Design nicht benötigt werden, ist eine Aktualisierung auf viele der unveränderlichen Sammlungen möglich , z. B. vorausgesetzt, dass Giraffe von Animal :

 IEnumerable animals = giraffes; 

Dies liegt daran, dass IEnumerable die Kovarianz in T unterstützt. Dies ist sinnvoll, da IEnumerable impliziert, dass die Auflistung nicht geändert werden kann, da sie keine Methoden zum Hinzufügen oder Entfernen von Elementen aus der Auflistung unterstützt. Beachten Sie das Schlüsselwort out in der Deklaration von IEnumerable :

 public interface IEnumerable : IEnumerable 

( Hier ist eine weitere Erklärung für den Grund, warum veränderbare Auflistungen wie List die covariance nicht unterstützen können, während unveränderliche Iteratoren und Auflistungen dies können.)

Casting mit .Cast()

Wie andere bereits erwähnt haben, kann .Cast() auf eine Sammlung angewendet werden, um eine neue Sammlung von Elementen zu InvalidCastException die an T übergeben werden. Dabei wird jedoch eine InvalidCastException wenn die InvalidCastException eines oder mehrerer Elemente nicht möglich ist Das gleiche Verhalten wie beim expliziten Cast in der foreach Schleife des OP.

Filtern und Casting mit OfType()

Wenn die Eingabeliste Elemente verschiedener, inkompatibler Typen enthält, kann die potenzielle InvalidCastException vermieden werden, indem .OfType() anstelle von .Cast() . ( .OfType<>() überprüft, ob ein Element vor der Konvertierung in den .OfType<>() konvertiert werden kann, und filtert inkompatible Typen heraus.)

für jede

Beachten Sie auch, dass wenn das OP dies stattdessen geschrieben hätte: (beachte das explizite Y y in der foreach )

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

dass das Casting auch versucht wird. Wenn jedoch keine InvalidCastException möglich ist, wird eine InvalidCastException .

Beispiele

Zum Beispiel angesichts der einfachen (C # 6) classnhierarchie:

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

Wenn Sie mit einer Sammlung verschiedener Typen arbeiten:

 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() // Also fails for Zed with `InvalidCastException .ToList(); 

Wohingegen:

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

filtert nur die Elefanten aus – dh Zebras werden eliminiert.

Betreff: Implizite Umsetzungsoperatoren

Ohne dynamic werden benutzerdefinierte Konvertierungsoperatoren nur zur Kompilierungszeit * verwendet. Selbst wenn ein Konvertierungsoperator zwischen Zebra und Elephant verfügbar gemacht wird, ändert sich das oben beschriebene Laufzeitverhalten der Konvertierungsansätze nicht.

Wenn wir einen Konvertierungsoperator hinzufügen, um einen Zebra in einen Elefanten umzuwandeln:

 public class Zebra : Animal { public Zebra(string name) : base(name) { } public static implicit operator Elephant(Zebra z) { return new Elephant(z.Name); } } 

Mit dem obigen Konvertierungsoperator kann der Compiler den Typ des folgenden Arrays von Animal[] in Elephant[] ändern, da die Zebras jetzt in eine homogene Sammlung von Elefanten konvertiert werden können:

 var compilerInferredAnimals = new [] { new Zebra("Zed"), new Elephant("Ellie") }; 

Implizite Konvertierungsoperatoren zur Laufzeit verwenden

* Wie von Eric erwähnt, kann der Konvertierungsoperator jedoch zur Laufzeit aufgerufen werden, indem auf dynamic :

 var mixedAnimals = new Animal[] // ie Polymorphic collection { new Zebra("Zed"), new Elephant("Ellie") }; foreach (dynamic animal in mixedAnimals) { castedAnimals.Add(animal); } // Returns Zed, Ellie 

Sie können List.ConvertAll([Converter from Y to T]);