En C#, pourquoi un objet List<string> ne peut-il pas être stocké dans une variable List<object>

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

Question

Il semble qu'un objet List ne puisse pas être stocké dans une variable List en C#, et ne puisse même pas être explicitement converti de cette façon.

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

aboutit à Impossible de convertir implicitement le type System.Collections.Generic.List<string> à System.Collections.Generic.List<object>

Et puis...

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

résultats dans Impossible de convertir le type System.Collections.Generic.List<string> à System.Collections.Generic.List<object>

Bien sûr, vous pouvez le faire en extrayant tout ce qui se trouve dans la liste de chaînes et en les remettant un par un, mais c'est une solution plutôt compliquée.

Était-ce utile?

La solution

Pensez-y de cette façon, si vous deviez effectuer une telle conversion, puis ajouter un objet de type Foo à la liste, la liste des chaînes n'est plus cohérente.Si vous deviez parcourir la première référence, vous obtiendrez une exception de conversion de classe car une fois que vous aurez atteint l'instance Foo, le Foo ne pourra pas être converti en chaîne !

En passant, je pense qu'il serait plus important que vous puissiez ou non faire le casting inversé :

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

Je n'ai pas utilisé C# depuis un moment, donc je ne sais pas si c'est légal, mais ce type de distribution est en fait (potentiellement) utile.Dans ce cas, vous passez d’une classe (objet) plus générale à une classe (chaîne) plus spécifique qui s’étend de la classe générale.De cette façon, si vous ajoutez à la liste des chaînes, vous ne violez pas la liste des objets.

Est-ce que quelqu'un sait ou peut tester si un tel casting est légal en C# ?

Autres conseils

Si vous utilisez .NET 3.5, jetez un œil à la méthode Enumerable.Cast.C'est une méthode d'extension, vous pouvez donc l'appeler directement sur la Liste.

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

Ce n'est pas exactement ce que vous avez demandé mais cela devrait faire l'affaire.

Modifier:Comme l'a noté Zooba, vous pouvez ensuite appeler ol.ToList() pour obtenir une liste

Vous ne pouvez pas effectuer de conversion entre des types génériques avec des paramètres de type différents.Les types génériques spécialisés ne font pas partie du même arbre d’héritage et sont donc des types non liés.

Pour ce faire avant NET 3.5 :

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
}

Utilisation de 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();

La raison est qu'une classe générique comme List<> est, dans la plupart des cas, traitée extérieurement comme une classe normale.par exemple.quand tu dis List<string>() le compilateur dit ListString() (qui contient des chaînes).[Personnes techniques :ceci est une version extrêmement simple et simplifiée de ce qui se passe]

Par conséquent, le compilateur ne peut évidemment pas être assez intelligent pour convertir un ListString en ListObject en convertissant les éléments de sa collection interne.

C'est pourquoi il existe des méthodes d'extension pour IEnumerable comme Convert() qui vous permettent de fournir facilement une conversion pour les éléments stockés dans une collection, ce qui peut être aussi simple que de passer de l'un à l'autre.

Cela a beaucoup à voir avec la covariance, par exemple, les types génériques sont considérés comme des paramètres, et si les paramètres ne se résolvent pas correctement en un type plus spécifique, l'opération échoue.L'implication de ceci est que vous ne pouvez vraiment pas convertir en un type plus général comme un objet.Et comme l'a indiqué Rex, l'objet List ne convertira pas chaque objet pour vous.

Vous voudrez peut-être essayer le code ff à la place :

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

ou:

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

ol copiera (théoriquement) tout le contenu de sl sans problème.

Oui, vous pouvez, à partir de .NET 3.5 :

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

Mike - Je crois que la contravariance n'est pas non plus autorisée en C#

Voir Variation des paramètres de type générique dans le CLR pour quelques informations supplémentaires.

Je pense que cela (contravariance) sera effectivement pris en charge en C# 4.0.http://blogs.msdn.com/charlie/archive/2008/10/27/linq-farm-covariance-and-contravariance-in-visual-studio-2010.aspx

C'est en fait pour que vous n'essayiez pas de mettre un "objet" étrange dans votre variante de liste "ancienne" (comme List<object> semblerait permettre) - parce que votre code planterait alors (parce que la liste est vraiment List<string> et n'acceptera que les objets de type String).C'est pourquoi vous ne pouvez pas convertir votre variable en une spécification plus générale.

Sur Java, c'est l'inverse, vous n'avez pas de génériques, et à la place, tout est une liste d'objets au moment de l'exécution, et vous pouvez vraiment insérer n'importe quel objet étrange dans votre liste soi-disant strictement typée.Recherchez "Génériques réifiés" pour voir une discussion plus large sur le problème de Java...

Une telle covariance sur les génériques n'est pas prise en charge, mais vous pouvez le faire avec des tableaux :

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

C# effectue des vérifications d'exécution pour vous empêcher de mettre, par exemple, un int dans a.

Voici une autre solution pré-.NET 3.5 pour toute IList dont le contenu peut être implicitement diffusé.

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

(Basé sur l'exemple de Zooba)

J'ai un:

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

Et j'allais le remplir avec des données collectées dans un List<object>Ce qui a finalement fonctionné pour moi, c'est celui-ci :

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

.Cast au type que vous souhaitez obtenir IEnumerable à partir de ce type, puis transtypez le IEnemuerable au List<> tu veux.

Mm, grâce aux commentaires précédents, j'ai trouvé deux façons de le savoir.La première consiste à obtenir la liste de chaînes d'éléments, puis à la convertir en liste d'objets IEnumerable :

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

Et la seconde consiste à éviter le type d'objet IEnumerable, en convertissant simplement la chaîne en type d'objet, puis en utilisant la fonction "toList()" dans la même phrase :

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

J'aime plus la deuxième façon.J'espère que ça aide.

List<string> sl = new List<string>();
List<object> ol;
ol = new List<object>(sl);
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top