2008-08-09 13 views
76

nicht gespeichert werden Es scheint, dass ein List-Objekt nicht in einer List-Variable in C# gespeichert werden kann und nicht einmal explizit auf diese Weise gegossen werden kann.In C#, warum kann ein List <string> Objekt in einer Liste <object> Variable

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

Ergebnisse in nicht implizit Typ System.Collections.Generic.List<string>-System.Collections.Generic.List<object>

Und dann ...

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

Ergebnisse in nicht-Typ System.Collections.Generic.List<string>-System.Collections.Generic.List<object>

Natürlich konvertieren kann, können Sie konvertieren kann indem man alles aus der String-Liste herauszieht und es nacheinander wieder einfügt, aber das ist es eine ziemlich komplizierte Lösung.

+5

Dies wird sich mit C# 4.0 ändern, also könnten Sie Kovarianz und Kontravarianz suchen. Es wird solche Dinge in einer Art sicheren Art erlauben. –

+0

Mehr oder weniger Duplikat: http://StackOverflow.com/Questions/317335/Why-Cani-I-NoT-Return-AlistFoo-If-Asked-for-a-Leififoo –

+7

John> C# 4 wird nicht zulassen Dies. Denken Sie an ol.Add (neues Objekt()); – Guillaume

Antwort

35

Denken Sie so vor, wenn Sie eine solche Umwandlung durchführen und dann ein Objekt vom Typ Foo zur Liste hinzufügen, ist die Liste der Strings nicht mehr konsistent. Wenn Sie die erste Referenz iterieren würden, würden Sie eine Klassen-Cast-Exception erhalten, denn wenn Sie die Foo-Instanz einmal getroffen haben, konnte Foo nicht in eine Zeichenfolge konvertiert werden!

Als Randbemerkung, ich denke, es von Bedeutung sein würde, ob Sie die umgekehrte Besetzung tun können:

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

Ich habe nicht C# in einer Weile benutzt, so dass ich weiß nicht, ob das ist legal, aber diese Art von Besetzung ist tatsächlich (potentiell) nützlich. In diesem Fall wechseln Sie von einer allgemeineren Klasse (Objekt) zu einer spezifischeren Klasse (Zeichenfolge), die sich von der allgemeinen Klasse erstreckt. Auf diese Weise verletzen Sie nicht die Liste der Objekte, wenn Sie der Liste der Zeichenfolgen hinzufügen.

Weiß jemand oder kann er testen, ob ein solcher Cast in C# zulässig ist?

+4

Ich denke, Ihr Beispiel leidet unter dem gleichen Problem. Was passiert, wenn ol etwas enthält, das keine Schnur ist? Das Problem ist, einige Methoden auf der Liste funktionieren gut, z. B. Hinzufügen/Einfügen. Aber das Iterieren könnte ein echtes Problem sein. –

+3

Eric Lippert hat eine große Reihe von Blog-Beiträgen zu diesem Thema: Warum könnte es funktionieren, um Kovarianz- und Kontravarianz-Contraints zu generischen Methoden hinzuzufügen, aber niemals so funktionieren, wie wir es auf Klassenebene möchten. http://is.gd/3kQc –

+0

@ChrisAmmerman: Wenn "ol" etwas in sich hat, das keine Zeichenkette ist, würde ich erwarten, dass die Umwandlung zur Laufzeit fehlschlägt. Aber wo du wirklich in Schwierigkeiten geraten würdest, wenn die Besetzung erfolgreich war, und * dann * etwas zu 'ol' hinzugefügt wurde, das ist keine Zeichenkette. Da 'sl' das gleiche Objekt referenziert, würde Ihre' Liste 'eine Nicht-Zeichenkette enthalten. Das 'Add' ist das Problem, das vermutlich rechtfertigt, warum dieser Code nicht kompiliert wird, aber er kompiliert, wenn Sie' List ol' in 'IEnumerable ol' ändern, das kein' Add' enthält. (Ich überprüfte dies in C# 4.) –

12

Der Grund ist, dass eine generische Klasse wie List<> für die meisten Zwecke extern als normale Klasse behandelt wird. z.B. Wenn Sie List<string>() sagen, sagt der Compiler ListString() (die Strings enthält). [Technical Folk: Dies ist eine extrem plain-Englisch-ified Version von dem, was ist los]

Konsequenterweise kann der Compiler nicht intelligent genug sein, um einen ListString zu einem ListObject zu konvertieren, indem er die Elemente seiner internen Sammlung umwandelt.

Aus diesem Grund gibt es Erweiterungsmethoden für IEnumerable wie Convert(), mit denen Sie einfach die Konvertierung für die in einer Sammlung gespeicherten Elemente bereitstellen können. Dies kann so einfach sein wie das Konvertieren von einem zum anderen.

6

Dies hat viel mit der Kovarianz zu tun, z. B. werden generische Typen als Parameter betrachtet, und wenn die Parameter nicht korrekt zu einem spezifischeren Typ aufgelöst werden, schlägt der Vorgang fehl. Die Folge davon ist, dass Sie wirklich nicht zu einem allgemeineren Typ wie Objekt umwandeln können. Und wie von Rex angegeben, konvertiert das List-Objekt nicht jedes Objekt für Sie.

Sie könnten stattdessen die ff-Code versuchen:

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) ohne Probleme den gesamten Inhalt von sl kopieren.

35

Wenn Sie .NET 3.5 verwenden, sehen Sie sich die Methode Enumerable.Cast an. Es ist eine Erweiterungsmethode, so dass Sie es 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, was Sie gefragt haben, aber sollte den Trick tun.

Edit: Wie Zooba erwähnt, können Sie dann ol.ToList() eine Liste

14

Sie können nicht gegossen zwischen generischen Typen mit verschiedenen Typparametern zu erhalten. Spezialisierte generische Typen sind nicht Teil desselben Vererbungsbaums und daher auch nicht verwandte Typen.

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

, die tatsächlich ist, so dass Sie jede ungerade setzen nicht versuchen, "Objekt" in Ihrem "ol"

Diese Pre-NET 3.5 zu tun Listenvariante (wie List<object> scheint zu erlauben) - weil Ihr Code dann abstürzen würde (weil die Liste wirklich List<string> ist und nur String-Typ-Objekte akzeptiert). Aus diesem Grund können Sie Ihre Variable nicht auf eine allgemeinere Spezifikation anwenden.

Auf Java ist es umgekehrt, Sie haben keine Generika und stattdessen ist alles Objektliste zur Laufzeit, und Sie können wirklich jedes seltsame Objekt in Ihrer vermeintlich streng typisierten Liste stopfen. Suche nach „Verdinglichte Generika“ eine breitere Diskussion über Java Problem zu sehen ...

1

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

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

C# führt Laufzeitprüfungen zu verhindern, dass Sie beispielsweise int in a setzen.

5

Ja, können Sie, von .NET 3.5:

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

Hier ist eine weitere pre.NET 3.5-Lösung für jede IList, deren Inhalt implizit umgewandelt werden kann.

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 Zooba des Beispiel)

0

Ich habe ein:

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

Und ich würde es in einem List<object> mit Daten füllen gesammelt Was schließlich für mich gearbeitet wurde diese :

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

.Cast es zum typ Wenn Sie eine IEnumerable von diesem Typ erhalten möchten, geben Sie die IEnemuerable an die List<>, die Sie wollen, ein.

0

Mm, dank der vorherigen Kommentare habe ich zwei Wege gefunden, es herauszufinden. Die erste ist immer die Zeichenfolge Liste der Elemente und dann auf IEnumerable Objektliste Gießen:

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

Und das zweite ist die IEnumerable Objekttyp zu vermeiden, nur die Zeichenfolge Gießen Typ Objekt und dann mit der Funktion " ToList()“im selben Satz:

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

ich mehr mag der zweite Weg. Ich hoffe das hilft.

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