Konvertieren Liste<DerivedClass> Liste<BaseClass>
Während wir Erben von der Basisklasse/Schnittstelle, warum können wir nicht erklären List<>
mit der gleichen Klasse/interface?
interface A
{ }
class B : A
{ }
class C : B
{ }
class Test
{
static void Main(string[] args)
{
A a = new C(); //OK
List<A> listOfA = new List<C>(); //compiler Error
}
}
Gibt es einen Weg, um?
InformationsquelleAutor Asad Butt | 2009-11-30
Schreibe einen Kommentar Antworten abbrechen
Du musst angemeldet sein, um einen Kommentar abzugeben.
Den Weg zu machen diese Arbeit ist die Iteration über die Liste und die Besetzung der Elemente. Dies kann mit ConvertAll:
Du könntest auch Linq:
Welches ist schneller?
ConvertAll
oderCast
?Dies erstellt eine Kopie der Liste. Wenn Sie hinzufügen oder entfernen von etwas, das in der neuen Liste, ist dieses nicht auch in der ursprünglichen Liste. Und zweitens, es ist eine große Leistung und Speicher Strafe seit es erstellt eine neue Liste mit den vorhandenen Objekten. Siehe meine Antwort weiter unten für eine Lösung ohne diese Probleme.
Antwort auf modiX: ConvertAll ist eine Methode, die auf List<T> , wird also nur funktionieren, auf eine generische Liste; es funktioniert nicht auf IEnumerable oder IEnumerable<T>; ConvertAll können auch benutzerdefinierte Konvertierungen nicht nur Gießen, z.B. ConvertAll(inches => Zoll * 25.4). Cast<A> ist eine LINQ-extension-Methode, so funktioniert auf jedem IEnumerable<T> (und funktioniert auch für nicht-generische IEnumerable), und wie die meisten von LINQ-it verwendet die verzögerte Ausführung, das heißt, der wandelt nur so viele items, wie abgerufen werden. Lesen Sie mehr über es hier: codeblog.jonskeet.uk/2011/01/13/...
InformationsquelleAutor Mark Byers
Erste von allen, stoppen Sie die Verwendung unmöglich zu verstehen, Klassennamen wie A, B, C. Benutzen Sie Tier, Säugetier, Giraffe oder Nahrung, Obst, Orange oder so etwas, wo die Beziehungen sind klar.
Ihre Frage dann "warum kann ich nicht zuweisen eine Liste der Giraffen an eine variable vom Typ Liste von Tier, da kann ich zuweisen eine giraffe an eine variable vom Typ Tier?"
Die Antwort ist: angenommen man könnte. Was könnte dann schief gehen?
Gut, Sie können hinzufügen, ein Tiger, eine Liste der Tiere. Nehmen wir an, wir ermöglichen es Ihnen, eine Liste von Giraffen in eine variable enthält eine Liste von Tieren. Dann Sie versuchen, fügen Sie einen tiger in die Liste ein. Was passiert? Wollen Sie die Liste der Giraffen zu enthalten, ein tiger? Wollen Sie einen crash? oder wollen Sie den compiler, um Sie zu schützen vor dem Absturz von der Abtretung in Erster Linie illegal?
Wählen wir die letztere.
Diese Art der Umwandlung nennt man eine "kovariante" - Konvertierung. In C# 4 wir können Sie machen, kovariante Konvertierungen auf Schnittstellen und Delegaten wenn die Konvertierung abgeschlossen ist, bekannt zu werden, immer sicher. Siehe meinen blog-Artikel über Kovarianz und Kontravarianz für details. (Es wird eine frische, die bei diesem Thema auf beide Montag und Donnerstag dieser Woche).
Während diese Antwort enthält durchaus akzeptabel Argumentation ist es nicht wirklich "true". Die einfache Antwort ist, dass C# nicht unterstützt. Die Idee, eine Liste von Tieren, enthält Giraffen und Tiger ist vollkommen gültig. Das einzige Problem kommt, wenn Sie wollen, um Zugang zu den höheren Kursen. In Wirklichkeit ist dies nichts anderes für die übergabe von übergeordneten Klasse als parameter an eine Funktion, und dann zu versuchen, zu werfen, es zu einem anderen Geschwister Klasse. Möglicherweise gibt es technische Probleme mit der Umsetzung der cast, aber die obige Erklärung nicht geben einen Grund, warum es wäre eine schlechte Idee.
Warum können wir diese Konvertierung mit einem
IEnumerable
statt einerList
? also:List<Animal> listAnimals = listGiraffes as List<Animal>;
ist nicht möglich, aberIEnumerable<Animal> eAnimals = listGiraffes as IEnumerable<Animal>
funktioniert.Lesen Sie den letzten Absatz meiner Antwort. Die Konvertierung vorhanden ist bekannt, sicher zu sein. Warum? Da es unmöglich ist, eine Sequenz von Giraffen in eine Sequenz von Tieren und dann auf setzen Sie einen tiger in der Reihenfolge der Tiere.
IEnumerable<T>
undIEnumerator<T>
sind beide als sicher markiert für die Kovarianz und der compiler überprüft hat.InformationsquelleAutor Eric Lippert
Zitieren, der die große Erklärung von Eric
Aber was ist, wenn Sie sich für eine Laufzeit crash statt einen compile-Fehler? Sie normalerweise verwenden würden, Cast<> oder ConvertAll<> aber dann wirst du 2 Probleme haben: Es wird eine Kopie der Liste. Wenn Sie hinzufügen oder entfernen von etwas, das in der neuen Liste, ist dieses nicht auch in der ursprünglichen Liste. Und zweitens, es ist eine große Leistung und Speicher Strafe seit es erstellt eine neue Liste mit den vorhandenen Objekten.
Ich hatte das gleiche problem und deshalb erstellte ich eine wrapper-Klasse casten kann eine generische Liste, ohne eine komplett neue Liste.
In der ursprünglichen Frage, dann könnten Sie verwenden:
und hier die wrapper-Klasse (+ eine Erweiterung Methode CastList für die einfache Nutzung)
Ich habe nur verwendet es als MVC-view-Modell und hast schöne universal razor Teilansicht. Tolle Idee! Ich bin so glücklich, dass ich dies Lesen.
Ich würde nur hinzufügen, ein "wo TTo : TFrom" bei der Klassendeklaration, so kann der compiler warnen vor falscher Verwendung. Machen Sie eine CastedList von nicht verwandten Arten ist sowieso unsinnig, und macht eine "CastedList<TBase, TDerived>" wäre sinnlos: Sie können nicht hinzufügen regelmäßige TBase-Objekte, und alle TDerived, die Sie von der original-Liste können bereits verwendet werden, als ein TBase.
Ach, ein sechs-Jahres-Vorsprung, der ist selbst Schuld.
der standard .Cast<T>() hat nicht diese Einschränkung. Ich wollte das Verhalten das gleiche wie .Cast, so ist es möglich zu casten ist eine Liste der Tiere, um eine Liste von Tigern, und eine Ausnahme, wenn es eine Giraffe zum Beispiel. Nur, wie wäre es mit Cast...
InformationsquelleAutor Bigjim
Soweit, warum es nicht funktioniert, könnte es hilfreich sein, zu verstehen,Kovarianz und Kontravarianz.
Nur um zu zeigen, warum diese sollte nicht Arbeit, hier ist eine änderung an dem code, den Sie zur Verfügung gestellt:
Soll das funktionieren? Das Erste Element in der Liste ist vom Typ "B", aber die Art der DerivedList Element ist C.
Nun davon ausgehen, dass wir wirklich wollen einfach nur, um eine generische Funktion, die arbeitet auf einer Liste der einige geben, die sich um Einen, aber wir kümmern uns nicht, welche Art das ist:
InformationsquelleAutor Chris Pitman
Wenn Sie
IEnumerable
statt, es wird funktionieren (zumindest in C# 4.0, ich habe nicht versucht, frühere Versionen). Dies ist nur ein cast, natürlich, es wird noch eine Liste.Statt -
List<A> listOfA = new List<C>(); //compiler Error
Im original-code von der Frage,
IEnumerable<A> listOfA = new List<C>(); //compiler error - no more! :)
Statt
List<A> listOfA = new List<C>(); // compiler Error
im original-code von der Frage, geben SieIEnumerable<A> listOfA = new List<C>(); // compiler error - no more! :)
Die Methode nehmen, die IEnumerable<BaseClass> als parameter für die geerbten Klasse übergeben werden, wie eine Liste. So gibt es sehr wenig zu tun, aber ändern Sie den parameter Typ.
InformationsquelleAutor PhistucK
Können Sie nur gegossen auf readonly-Listen. Zum Beispiel:
Und Sie es nicht für Listen, die Unterstützung speichern von Elementen. Der Grund ist:
Was nun? Denken Sie daran, dass listObject und listString sind der gleichen Liste eigentlich so listString haben jetzt object-element, es sollte nicht möglich sein und das ist es nicht.
Es ist eine Schande, dass Sie nicht tun können, ähnlich wie bei IReadOnlyDictionary<TKey, TValue>. Warum ist das so?
Das ist ein toller Vorschlag. Ich benutze List<> überall für Komfort, aber die meisten der Zeit, ich will, dass es readonly. Schöne Anregung, wie diese zu verbessern wird mein code auf 2 Arten, indem diese Besetzung und die Verbesserung, wo ich angeben readonly.
InformationsquelleAutor Wojciech Mikołajewicz
Ich persönlich gerne erstellen libs mit Erweiterungen der Klassen
InformationsquelleAutor vikingfabian
Weil C# nicht zulassen, dass Art von
VererbungUmwandlung im moment.Gut, ich kann kaum mit Ihnen streiten.
InformationsquelleAutor Noon Silk
Dies ist eine Erweiterung des BigJim ist genial Antwort.
In meinem Fall hatte ich eine
NodeBase
Klasse mit einemChildren
Wörterbuch, und ich brauchte einen Weg, um generisch O(1) - lookups von den Kindern. Ich war versucht, zurück ein eigenes dictionary-Feld in der getter vonChildren
, so offensichtlich wollte ich vermeiden teuer kopieren/verändern. Deshalb habe ich Bigjim code zum cast derDictionary<whatever specific type>
zu einem generischenDictionary<NodeBase>
:Das funktionierte gut. Jedoch ich schließlich lief in beziehungslose Grenzen und am Ende die Schaffung eines abstrakten
FindChild()
Methode in der Basisklasse, die tun würde, die lookups statt. Es stellte sich heraus, dies eliminiert die Notwendigkeit für die gegossenen Wörterbuch in den ersten Platz. (Ich war in der Lage, Sie zu ersetzen mit einem einfachenIEnumerable
für meine Zwecke.)Also die Frage, die Sie stellen könnten (vor allem wenn die Leistung ein Problem, verbieten Sie von der Nutzung
.Cast<>
oder.ConvertAll<>
):"Muss ich wirklich brauchen, um zu werfen die gesamte Kollektion, oder kann ich eine abstrakte Methode zu halten, die Besondere Kenntnisse erforderlich, um die Aufgabe zu erfüllen und dabei vermeiden, direkt auf die Sammlung?"
Manchmal ist die einfachste Lösung die beste ist.
InformationsquelleAutor Zach