c# summary - Impossible de convertir de la liste<DerivedClass>en liste<BaseClass>




documentation visual (6)

Les explications que j'ai trouvées ont simplement dit que cela violait en quelque sorte la sécurité de type, mais je ne le vois pas. Quel est le risque que le compilateur autorise la conversion de List<DerivedClass> en List<BaseClass> ?

Cette question est posée presque tous les jours.

Une List<Mammal> ne peut pas être convertie en une List<Animal> car vous pouvez placer un lézard dans une liste d’animaux . Une List<Mammal > ne peut pas être convertie en une List<Giraffe> car il pourrait y avoir déjà un tigre dans la liste .

Par conséquent, List<T> doit être invariant dans T.

Cependant, List<Mammal> peut être converti en IEnumerable<Animal> (à partir de C # 4.0) car il n’existe aucune méthode sur IEnumerable<Animal> qui ajoute un lézard. IEnumerable<T> est covariant dans T.

J'essaie de passer une liste de DerivedClass à une fonction qui prend une liste de BaseClass , mais j'obtiens l'erreur:

cannot convert from 
'System.Collections.Generic.List<ConsoleApplication1.DerivedClass>' 
to 
'System.Collections.Generic.List<ConsoleApplication1.BaseClass>'

Maintenant, je pourrais List<DerivedClass> ma List<DerivedClass> en une List<BaseClass> , mais je ne me sens pas à l’aise à moins de comprendre pourquoi le compilateur ne le permet pas.

Les explications que j'ai trouvées ont simplement dit que cela violait en quelque sorte la sécurité de type, mais je ne le vois pas. Quelqu'un peut m'aider?

Quel est le risque que le compilateur autorise la conversion de List<DerivedClass> en List<BaseClass> ?

Voici mon SSCCE:

class Program
{
    public static void Main()
    {
        BaseClass bc = new DerivedClass(); // works fine
        List<BaseClass> bcl = new List<DerivedClass>(); // this line has an error

        doSomething(new List<DerivedClass>()); // this line has an error
    }

    public void doSomething(List<BaseClass> bc)
    {
        // do something with bc
    }
}

class BaseClass
{
}

class DerivedClass : BaseClass
{
}

Lorsque vous avez une classe dérivée d'une classe de base, tous les conteneurs de ces classes ne sont pas automatiquement dérivés. Vous ne pouvez donc pas simplement lancer une List<Derived> dans une List<Base> .

Utilisez .Cast<T>() pour créer une nouvelle liste où chaque objet est renvoyé à la classe de base:

List<MyDerived> list1 = new List<MyDerived>();
List<MyBase> list2 = list1.Cast<MyBase>().ToList();

Notez qu'il s'agit d'une nouvelle liste, et non d'une version de distribution de votre liste d'origine. Les opérations sur cette nouvelle liste ne seront donc pas reflétées dans la liste d'origine. Les opérations sur les objets contenus refléteront cependant.


Si tu pouvais écrire

List<BaseClass> bcl = new List<DerivedClass>(); 

vous pourriez appeler

var instance = new AnotherClassInheritingFromBaseClass();
bc1.Add(instance);

Ajout d'une instance qui n'est pas une DerivedClass à la liste.


C'est parce que List<T> est in-variant , pas co-variant , donc vous devriez changer pour IEnumerable<T> qui supporte le co-variant , ça devrait fonctionner:

IEnumerable<BaseClass> bcl = new List<DerivedClass>();
public void doSomething(IEnumerable<BaseClass> bc)
{
    // do something with bc
}

Informations sur la co-variante en générique


Le comportement que vous décrivez est appelé covariance - si A est B , alors List<A> est List<B> .

Cependant, pour les types mutables comme List<T> , cela est fondamentalement dangereux.

Si cela avait été possible, la méthode pourrait ajouter un new OtherDerivedClass() à une liste qui ne peut contenir que DerivedClass .

La covariance est sûre sur les types immuables, même si .Net ne le prend en charge que dans les interfaces et les délégués.
Si vous modifiez le paramètre List<T> en IEnumerable<T> , cela fonctionnera


Il est si étrange que personne ne renvoie à l'entrée de blog What AnyCPU Really Means As. NET 4.5 et Visual Studio 11 :

Dans .NET 4.5 et Visual Studio 11, le fromage a été déplacé. La valeur par défaut pour la plupart des projets .NET est à nouveau AnyCPU, mais AnyCPU a maintenant plus d'une signification. Il existe un sous-type supplémentaire de AnyCPU, "Any CPU 32-bit preferred", qui est la nouvelle valeur par défaut (globalement, il y a maintenant cinq options pour le commutateur / platform C #: x86, Itanium, x64, anycpu et anycpu32bitpreferred ). Lors de l'utilisation de la saveur "Perfer 32-Bit" de AnyCPU, la sémantique est la suivante:

  • Si le processus s'exécute sur un système Windows 32 bits, il s'exécute en tant que processus 32 bits. IL est compilé en code machine x86.
  • Si le processus s'exécute sur un système Windows 64 bits, il s'exécute en tant que processus 32 bits. IL est compilé en code machine x86.
  • Si le processus s'exécute sur un système Windows ARM, il s'exécute en tant que processus 32 bits. IL est compilé avec le code machine ARM.

La différence entre "Any CPU 32-bit preferred" et "x86" est la suivante: une application .NET compilée en x86 ne fonctionnera pas sur un système ARM Windows, mais sur une application "Any CPU 32-bit preferred" va fonctionner avec succès.






c#  

c#