Il semble qu'un objet List ne peut pas être stocké dans une variable List en C #, et ne peut même pas être explicitement converti de cette façon.
List<string> sl = new List<string>();
List<object> ol;
ol = sl;
résultats dans Impossible de convertir implicitement le type System.Collections.Generic.List<string>
enSystem.Collections.Generic.List<object>
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>
enSystem.Collections.Generic.List<object>
Bien sûr, vous pouvez le faire en retirant tout de la liste de chaînes et en le remettant un à la fois, mais c'est une solution plutôt compliquée.
c#
.net
generics
covariance
type-safety
Matt Sheppard
la source
la source
Réponses:
Pensez-y de cette façon, si vous deviez faire un tel cast, puis ajouter un objet de type Foo à la liste, la liste des chaînes n'est plus cohérente. Si vous répétez la première référence, vous obtiendrez une exception de conversion de classe car une fois que vous avez atteint l'instance Foo, le Foo ne peut pas être converti en chaîne!
En passant, je pense qu'il serait plus important de savoir si vous pouvez ou non faire la fonte inversée:
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 genre de distribution est en fait (potentiellement) utile. Dans ce cas, vous passez d'une classe plus générale (objet) à une classe plus spécifique (chaîne) 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.
Quelqu'un sait-il ou peut-il tester si une telle distribution est légale en C #?
la source
ol
avait quelque chose dedans qui n'est pas une chaîne, je suppose que je m'attendrais à ce que la distribution échoue à l'exécution. Mais là où vous auriez vraiment des problèmes, c'est si le casting réussissait, puis quelque chose a été ajouté à ceol
qui n'est pas une chaîne. Parce que faitsl
référence au même objet, maintenant votreList<string>
contiendrait une non-chaîne. C'estAdd
le problème, qui, je suppose, justifie pourquoi ce code ne se compilera pas, mais il se compilera si vous passezList<object> ol
àIEnumerable<object> ol
, qui n'a pas de fichierAdd
. (J'ai vérifié cela en C # 4.)InvalidCastException
car le type d'exécution deol
est toujoursList<object>
.Si vous utilisez .NET 3.5, jetez un œil à la méthode Enumerable.Cast. C'est une méthode d'extension afin que vous puissiez 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.
Edit: Comme indiqué par Zooba, vous pouvez ensuite appeler ol.ToList () pour obtenir une liste
la source
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 de la même arborescence d'héritage, de même que les types non liés.
Pour ce faire pré-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 source
La raison en est qu'une classe générique comme
List<>
est, dans la plupart des cas, traitée en externe comme une classe normale. par exemple, quand vous dites queList<string>()
le compilateur ditListString()
(qui contient des chaînes). [Techniciens: ceci est une version extrêmement simple en anglais 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 pourrait être aussi simple que de passer de l'un à l'autre.
la source
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 d'un tel est que vous ne pouvez vraiment pas convertir en un type plus général comme object. Et comme indiqué par 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.
la source
Oui, vous pouvez, à partir de .NET 3.5:
List<string> sl = new List<string>(); List<object> ol = sl.Cast<object>().ToList();
la source
Mike - Je crois que la contravariance n'est pas non plus autorisée en C #
Voir Variance des paramètres de type générique dans le CLR pour plus d'informations.
la source
Je pense que cette (contravariance) sera en fait prise en charge dans C # 4.0. http://blogs.msdn.com/charlie/archive/2008/10/27/linq-farm-covariance-and-contravariance-in-visual-studio-2010.aspx
la source
C'est en fait pour que vous n'essayiez pas de mettre un "objet" étrange dans votre variante de liste "ol" (comme
List<object>
cela semble le permettre) - parce que votre code planterait alors (parce que la liste est vraimentList<string>
et n'acceptera que des 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 Liste d'objets au moment de l'exécution, et vous pouvez vraiment insérer n'importe quel objet étrange dans votre liste supposée strictement typée. Recherchez "Reified generics" pour voir une discussion plus large sur le problème de Java ...
la source
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 à l'exécution pour vous empêcher de mettre, disons, un
int
dansa
.la source
Voici une autre solution pré-.NET 3.5 pour tout IList dont le contenu peut être casté implicitement.
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)
la source
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 était celui-ci:.Cast
vers le type que vous souhaitez obtenir àIEnumerable
partir de ce type, puis transtypez leIEnemuerable
dans le typeList<>
souhaité.la source
Mm, grâce aux commentaires précédents, j'ai trouvé deux façons de le découvrir. Le premier consiste à obtenir la liste de chaînes des éléments, puis à la convertir en liste d'objets IEnumerable:
IEnumerable<object> ob; List<string> st = new List<string>(); ob = st.Cast<object>();
Et le second évite 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 seconde façon. J'espère que ça aide.
la source
List<string> sl = new List<string>(); List<object> ol; ol = new List<object>(sl);
la source