Warum kann in C# ein List<string>-Objekt nicht in einer List<object>-Variablen gespeichert werden?

StackOverflow https://stackoverflow.com/questions/6557

Frage

Es scheint, dass ein List-Objekt in C# nicht in einer List-Variablen gespeichert und nicht einmal explizit auf diese Weise umgewandelt werden kann.

List<string> sl = new List<string>();
List<object> ol;
ol = sl;

führt dazu, dass der Typ nicht implizit konvertiert werden kann System.Collections.Generic.List<string> Zu System.Collections.Generic.List<object>

Und dann...

List<string> sl = new List<string>();
List<object> ol;
ol = (List<object>)sl;

führt dazu, dass der Typ nicht konvertiert werden kann System.Collections.Generic.List<string> Zu System.Collections.Generic.List<object>

Natürlich können Sie dies tun, indem Sie alles aus der String-Liste herausziehen und einzeln wieder einfügen, aber das ist eine ziemlich komplizierte Lösung.

War es hilfreich?

Lösung

Stellen Sie sich das so vor: Wenn Sie eine solche Umwandlung durchführen und dann ein Objekt vom Typ Foo zur Liste hinzufügen würden, wäre die Liste der Zeichenfolgen nicht mehr konsistent.Wenn Sie die erste Referenz iterieren würden, würden Sie eine Klassenumwandlungsausnahme bekommen, denn sobald Sie die Foo-Instanz erreichten, konnte Foo nicht in einen String konvertiert werden!

Als Randbemerkung denke ich, dass es wichtiger wäre, ob Sie die umgekehrte Besetzung durchführen können oder nicht:

List<object> ol = new List<object>();
List<string> sl;
sl = (List<string>)ol;

Ich habe C# schon eine Weile nicht mehr verwendet, daher weiß ich nicht, ob das legal ist, aber diese Art der Umwandlung ist tatsächlich (potenziell) nützlich.In diesem Fall gehen Sie von einer allgemeineren Klasse (Objekt) zu einer spezifischeren Klasse (String) über, die von der allgemeinen Klasse ausgeht.Wenn Sie also zur Liste der Zeichenfolgen etwas hinzufügen, verletzen Sie nicht die Liste der Objekte.

Weiß jemand oder kann jemand testen, ob eine solche Umwandlung in C# legal ist?

Andere Tipps

Wenn Sie .NET 3.5 verwenden, schauen Sie sich die Enumerable.Cast-Methode an.Es handelt sich um eine Erweiterungsmethode, sodass Sie sie direkt in der Liste aufrufen können.

List<string> sl = new List<string>();
IEnumerable<object> ol;
ol = sl.Cast<object>();

Es ist nicht genau das, wonach Sie gefragt haben, aber es sollte den Zweck erfüllen.

Bearbeiten:Wie von Zooba angemerkt, können Sie dann ol.ToList() aufrufen, um eine Liste abzurufen

Sie können nicht zwischen generischen Typen mit unterschiedlichen Typparametern umwandeln.Spezialisierte generische Typen sind nicht Teil desselben Vererbungsbaums und daher nicht verwandte Typen.

Gehen Sie dazu vor NET 3.5 wie folgt vor:

List<string> sl = new List<string>();
// Add strings to sl

List<object> ol = new List<object>();

foreach(string s in sl)
{
    ol.Add((object)s);  // The cast is performed implicitly even if omitted
}

Mit Linq:

var sl = new List<string>();
// Add strings to sl

var ol = new List<object>(sl.Cast<object>());

// OR
var ol = sl.Cast<object>().ToList();

// OR (note that the cast to object here is required)
var ol = sl.Select(s => (object)s).ToList();

Der Grund dafür ist, dass eine generische Klasse wie List<> wird für die meisten Zwecke äußerlich als normale Klasse behandelt.z.B.wenn du sagst List<string>() sagt der Compiler ListString() (die Zeichenfolgen enthält).[Techniker:Dies ist eine äußerst einfach englischsprachige Version dessen, was vor sich geht.]

Folglich kann der Compiler offensichtlich nicht intelligent genug sein, um einen ListString in ein ListObject umzuwandeln, indem er die Elemente seiner internen Sammlung umwandelt.

Aus diesem Grund gibt es Erweiterungsmethoden für IEnumerable wie Convert(), mit denen Sie problemlos eine Konvertierung für die in einer Sammlung gespeicherten Elemente bereitstellen können, was so einfach sein kann wie das Umwandeln von einem in ein anderes.

Dies hat viel mit Kovarianz zu tun, z. B. werden generische Typen als Parameter betrachtet, und wenn die Parameter nicht ordnungsgemäß in einen spezifischeren Typ aufgelöst werden, schlägt die Operation fehl.Dies bedeutet, dass Sie wirklich nicht in einen allgemeineren Typ wie ein Objekt umwandeln können.Und wie von Rex angegeben, konvertiert das List-Objekt nicht jedes Objekt für Sie.

Vielleicht möchten Sie stattdessen den ff-Code ausprobieren:

List<string> sl = new List<string>();
//populate sl
List<object> ol = new List<object>(sl);

oder:

List<object> ol = new List<object>();
ol.AddRange(sl);

ol wird (theoretisch) den gesamten Inhalt von sl ohne Probleme kopieren.

Ja, ab .NET 3.5 ist Folgendes möglich:

List<string> sl = new List<string>();
List<object> ol = sl.Cast<object>().ToList();

Mike – Ich glaube, dass Kontravarianz auch in C# nicht erlaubt ist

Sehen Generische Typparametervarianz in der CLR für weitere Informationen.

Ich denke, dass dies (Kontravarianz) tatsächlich in C# 4.0 unterstützt wird.http://blogs.msdn.com/charlie/archive/2008/10/27/linq-farm-covariance-and-contravariance-in-visual-studio-2010.aspx

Das ist eigentlich so, dass Sie nicht versuchen, irgendein seltsames „Objekt“ in Ihre „alte“ Listenvariante einzufügen (wie List<object> scheint zu erlauben) - weil Ihr Code dann abstürzen würde (weil die Liste es wirklich ist). List<string> und akzeptiert nur Objekte vom Typ String).Aus diesem Grund können Sie Ihre Variable nicht in eine allgemeinere Spezifikation umwandeln.

Auf Java ist es umgekehrt, Sie haben keine Generika, und stattdessen ist alles zur Laufzeit eine Liste von Objekten, und Sie können wirklich jedes seltsame Objekt in Ihre vermeintlich streng typisierte Liste stopfen.Suchen Sie nach „Reified generics“, um eine ausführlichere Diskussion des Java-Problems zu sehen ...

Eine solche Kovarianz bei Generika wird nicht unterstützt, aber Sie können dies tatsächlich mit Arrays tun:

object[] a = new string[] {"spam", "eggs"};

C# führt Laufzeitprüfungen durch, um zu verhindern, dass Sie beispielsweise eine setzen int hinein a.

Hier ist eine weitere Lösung vor .NET 3.5 für jede IList, deren Inhalte implizit umgewandelt werden können.

public IList<B> ConvertIList<D, B>(IList<D> list) where D : B
{
    List<B> newList = new List<B>();

    foreach (D item in list)
    {
        newList.Add(item);
    }

    return newList;
}

(Basierend auf Zoobas Beispiel)

Ich habe ein:

private List<Leerling> Leerlingen = new List<Leerling>();

Und ich wollte es mit Daten füllen, die in einem gesammelt wurden List<object>Was bei mir letztendlich funktioniert hat, war Folgendes:

Leerlingen = (List<Leerling>)_DeserialiseerLeerlingen._TeSerialiserenObjecten.Cast<Leerling>();

.Cast es auf den Typ, den Sie erhalten möchten IEnumerable von diesem Typ, dann typisieren Sie die IEnemuerable zum List<> Sie wollen.

Mm, dank früherer Kommentare habe ich zwei Möglichkeiten gefunden, es herauszufinden.Die erste besteht darin, die String-Liste der Elemente abzurufen und sie dann in die IEnumerable-Objektliste umzuwandeln:

IEnumerable<object> ob;
List<string> st = new List<string>();
ob = st.Cast<object>();

Und die zweite Möglichkeit besteht darin, den Objekttyp IEnumerable zu vermeiden, einfach die Zeichenfolge in den Objekttyp umzuwandeln und dann die Funktion „toList()“ im selben Satz zu verwenden:

List<string> st = new List<string>();
List<object> ob = st.Cast<object>().ToList();

Der zweite Weg gefällt mir besser.Ich hoffe das hilft.

List<string> sl = new List<string>();
List<object> ol;
ol = new List<object>(sl);
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top