2009-05-20 14 views
1

Comment puis-je faire fonctionner le code suivant? Je ne pense pas que je comprends tout à fait les génériques C#. Peut-être que quelqu'un peut me diriger dans la bonne direction.Comment faire une classe générique avec l'héritage?

public abstract class A 
    { 
    } 

    public class B : A 
    { 
    } 

    public class C : A 
    { 
    } 

    public static List<C> GetCList() 
    { 
     return new List<C>(); 
    } 

    static void Main(string[] args) 
    { 
     List<A> listA = new List<A>(); 

     listA.Add(new B()); 
     listA.Add(new C()); 

     // Compiler cannot implicitly convert 
     List<A> listB = new List<B>(); 

     // Compiler cannot implicitly convert 
     List<A> listC = GetCList(); 

     // However, copying each element is fine 
     // It has something to do with generics (I think) 
     List<B> listD = new List<B>(); 
     foreach (B b in listD) 
     { 
      listB.Add(b); 
     } 
    } 

C'est probablement une réponse simple.

Mise à jour: D'abord, cela n'est pas possible en C# 3.0, mais sera possible en C# 4.0.

Pour le faire fonctionner en C# 3.0, qui est juste une solution de contournement jusqu'à ce que 4.0, utilisez les touches suivantes:

 // Compiler is happy 
     List<A> listB = new List<B>().OfType<A>().ToList(); 

     // Compiler is happy 
     List<A> listC = GetCList().OfType<A>().ToList(); 
+0

Ce n'est pas vraiment inhérent aux génériques, mais la réponse de Spence est à peu près aussi bonne que vous obtiendrez. C# 4.0 ajoutera "dactylographie", où vous pourrez vous en sortir avec ce genre de chose. – womp

+5

Ceci est incorrect. Nous N'ajoutons PAS le typage du canard, et la covariance et la contravariance ne s'appliquent qu'aux interfaces; Liste est une classe. –

Répondre

3

vous pouvez toujours faire

List<A> testme = new List<B>().OfType<A>().ToList(); 

Comme "Bojan Resnik" a souligné , vous pouvez aussi le faire ...

List<A> testme = new List<B>().Cast<A>().ToList(); 

Une différence à noter est que Cast <T>() échouera si un ou plusieurs des types ne correspondent pas. Où OfType <T>() retourne un IEnumerable <T> ne contenant que les objets qui sont convertibles

+0

Merci. Cela rend possible en C# 3.0. Cependant, y a-t-il un traitement supplémentaire/surcharge avec les appels supplémentaires? – kevindaub

+0

Oui ... mais si vous deviez le rendre IEnumerable ou IQueryable ce ne serait pas aussi mauvais. –

+0

oftype vous oblige à énumérer la liste, n'est-ce pas? Si vous avez contravariance alors les appels de méthode peuvent être liés par le système de type pas via l'énumération et seront considérablement plus rapides à condition que vous n'avez pas besoin de la distribution? – Spence

4

La raison pour laquelle cela ne fonctionne pas est parce qu'il ne peut être déterminée pour être sûr. Supposons que vous ayez

List<Giraffe> giraffes = new List<Giraffe>(); 
List<Animal> animals = giraffes; // suppose this were legal. 
// animals is now a reference to a list of giraffes, 
// but the type system doesn't know that. 
// You can put a turtle into a list of animals... 
animals.Add(new Turtle()); 

Et hey, vous venez de mettre une tortue dans une liste des girafes, et l'intégrité du système de type a été violé. C'est pourquoi c'est illégal.

La clé ici est que "animaux" et "girafes" se réfèrent au même objet, et cet objet est une liste de girafes. Mais une liste de girafes ne peut pas faire autant qu'une liste d'animaux peut faire; en particulier, il ne peut contenir une tortue.

+0

Je suppose que je ne comprends pas pourquoi il est illégal de placer B et C dans leur classe de base abstraite. – kevindaub

+0

J'ai aussi du mal à comprendre cela: vous avez mis une tortue dans une liste d'animaux qui ne contenait que des girafes. Pourquoi cela devrait-il être un problème? Les tortues et les girafes partageront les membres animaux, non? Le compilateur devrait se plaindre quand vous essayez de lancer la liste des animaux à une liste de girafes. – Dabblernl

+0

Cela serait différent de simplement essayer d'obtenir une collection d'animaux afin que vous puissiez appeler la méthode abstraite .Walk() sur chacun d'eux. Si vous voulez faire un travail commun et ne pas mélanger les types, ce n'est pas un problème. C'est pourquoi il est probablement préférable de simplement créer une nouvelle liste ou de n'utiliser la liste que comme IEnumerable <> –

Questions connexes