C # Casting une liste en tant que liste

Pourquoi ne puis-je pas lancer une List tant que List ? Pourquoi ce qui suit ne fonctionne pas:

 internal class ObjBase { } internal class Obj : ObjBase { } internal class ObjManager { internal List returnStuff() { return getSomeStuff() as List; } private List getSomeStuff() { return new List(); } } 

Au lieu de cela, je dois faire ceci:

 internal class ObjBase { } internal class Obj : ObjBase { } internal class ObjManager { internal List returnStuff() { List returnedList = getSomeStuff(); List listToReturn = new List(returnedList.Count); foreach (ObjBase currentBaseObject in returnedList) { listToReturn.Add(currentBaseObject as Obj); } return listToReturn; } private List getSomeStuff() { return new List(); } } 

J’obtiens l’erreur suivante dans Visual Studio 2008 (raccourci pour la lisibilité):

Impossible de convertir le type ‘List’ en ‘List’ via une conversion de référence, une conversion de boxe, une conversion de unboxing, une conversion d’habillage ou une conversion de type null

Merci.

Veuillez regarder les questions suivantes:

Liste générique .NET Casting

Pourquoi cette dissortingbution générique échoue?

Vous pouvez utiliser les méthodes d’extension Cast et ToList partir de System.Linq pour l’afficher sur une seule ligne.

Au lieu de

 internal List returnStuff() { return getSomeStuff() as List; } 

faire ceci:

 internal List returnStuff() { return getSomeStuff().Cast().ToList(); } 

Je peux seulement décrire le “problème” à partir d’une vue Java, mais de ce que je sais peu, cet aspect est le même en C # et en Java:

Une List n’est pas une List , car elle pourrait contenir un object ObjBase qui n’est pas un object Obj .

Le contraire d’ une List ne peut pas être List une List car le premier garantit l’acceptation d’un appel Add() avec un argument ObjBase , ce que le dernier n’acceptera pas!

Donc, pour résumer: même si un object est un ObjBase une List n’est pas une List .

Je pense que vous comprenez mal le casting que vous essayez de faire. Vous pensez que vous modifiez le type de l’object stocké dans la liste, alors que vous essayez de modifier le type de la liste elle-même. Il est plutôt logique que vous ne puissiez pas modifier la liste telle que vous l’avez déjà remplie.

Vous pouvez la voir comme une liste d’une classe de base, puis la lancer lorsque vous traitez les éléments de la liste. Ce serait mon approche.

Quel est le but de cette tentative de casting?

C # ne prend actuellement pas en charge la variance pour les types génériques. D’après ce que j’ai lu, cela va changer en 4.0.

Voir ici pour plus d’informations sur la variance des génériques.

list.ConvertAll semble tentant, mais a un gros inconvénient: il va créer une toute nouvelle liste. Cela affectera les performances et l’utilisation de la mémoire, en particulier pour les grandes listes.

Avec un peu plus d’effort, vous pouvez créer une classe de liste wrapper qui conserve la liste d’origine en tant que référence interne et convertit les éléments uniquement lorsqu’ils sont utilisés.

Usage:

 var x = new List(); var y = x.CastList(); // y is now an IList 

Code à append à votre bibliothèque:

 public static class Extensions { public static IList CastList(this IList list) { return new CastedList(list); } } public class CastedList : IList { public IList BaseList; public CastedList(IList baseList) { BaseList = baseList; } // IEnumerable IEnumerator IEnumerable.GetEnumerator() { return BaseList.GetEnumerator(); } // IEnumerable<> public IEnumerator GetEnumerator() { return new CastedEnumerator(BaseList.GetEnumerator()); } // ICollection public int Count { get { return BaseList.Count; } } public bool IsReadOnly { get { return BaseList.IsReadOnly; } } public void Add(TTo item) { BaseList.Add((TFrom)(object)item); } public void Clear() { BaseList.Clear(); } public bool Contains(TTo item) { return BaseList.Contains((TFrom)(object)item); } public void CopyTo(TTo[] array, int arrayIndex) { BaseList.CopyTo((TFrom[])(object)array, arrayIndex); } public bool Remove(TTo item) { return BaseList.Remove((TFrom)(object)item); } // IList public TTo this[int index] { get { return (TTo)(object)BaseList[index]; } set { BaseList[index] = (TFrom)(object)value; } } public int IndexOf(TTo item) { return BaseList.IndexOf((TFrom)(object)item); } public void Insert(int index, TTo item) { BaseList.Insert(index, (TFrom)(object)item); } public void RemoveAt(int index) { BaseList.RemoveAt(index); } } public class CastedEnumerator : IEnumerator { public IEnumerator BaseEnumerator; public CastedEnumerator(IEnumerator baseEnumerator) { BaseEnumerator = baseEnumerator; } // IDisposable public void Dispose() { BaseEnumerator.Dispose(); } // IEnumerator object IEnumerator.Current { get { return BaseEnumerator.Current; } } public bool MoveNext() { return BaseEnumerator.MoveNext(); } public void Reset() { BaseEnumerator.Reset(); } // IEnumerator<> public TTo Current { get { return (TTo)(object)BaseEnumerator.Current; } } } 

Linq a une méthode ConvertAll. donc quelque chose comme

 list.ConvertAll(objBase => objbase.ConvertTo(obj)); 

Je ne sais pas quoi suggérer d’autre. Je suppose que ObjBase est la classe de base, et si tous les objects ObjBase sont des objects Obj, je ne suis pas sûr de savoir pourquoi vous auriez les deux objects en premier lieu. Peut-être que je suis hors de propos.

Edit: la méthode list.Cast fonctionnerait mieux que la méthode ci-dessus, en supposant qu’elles soient transposables les unes aux autres. J’ai oublié ça jusqu’à ce que je lise les autres réponses.

C’est une douleur majeure en C # – c’est comment les génériques ont été conçus. La liste ne prolonge pas la liste, c’est juste un type complètement différent. Vous ne pouvez en aucun cas les atsortingbuer ou les atsortingbuer, votre seule option est de copier une liste sur une autre.

Lazarus :
Je pensais que le compilateur comprendrait que je voulais que des actions soient effectuées sur les objects de la liste et non que j’essayais de lancer la liste elle-même.

Quelques informations supplémentaires:

 public abstract class ObjBase { } internal interface IDatabaseObject { } public class Obj : ObjBase, IDatabaseObject { } internal interface IDatabaseObjectManager { List getSomeStuff(); } public class ObjManager : IObjManager { public List returnStuff() { return getSomeStuff().Cast ().ToList(); } private List getSomeStuff() { return new List(); } } 

Le code client en dehors de cette DLL peut maintenant aller: ObjManager objM = new ObjManager (); List listOB = objM.returnStuff (); Je vais créer plusieurs types Obj et ObjManager pour cette partie (O / RM) de l’application.

(Darn bloc de commentaires à court de caractères! 🙂

Voici comment j’ai corrigé la conversion d’un

 list 

à un

 object 

et ensuite à un

 List 

https://stackoverflow.com/a/16147909/2307326