2010-08-05 7 views
4

Ok, j'ai quelques objets différents qui sont dérivés d'une classe de base et j'en ai mis beaucoup dans une liste. Je veux parcourir la liste et pousser chacun à une méthode. J'ai des méthodes séparées avec la signature de chaque type, mais le compilateur se plaint. Quelqu'un peut-il expliquer pourquoi? Est-ce une opportunité d'utiliser des génériques, et si oui, comment?Classes dérivées C#, résolution de surcharge

class Base { } 
class Level1 : Base { } 
class Level2 : Level1 { } 

...

List<Base> oList = new List<Base>(); 
oList.Add(new Level1()); 
oList.Add(new Level2()); 

...

... 
foreach(Base o in oList) 
{ 
    DoMethod(o); 
} 

...

void DoMethod(Level1 item) { } 
void DoMethod(Level2 item) { } 

Qu'est-ce que je fais mal?

+0

Je ne suis pas sûr que ce soit clair et je pense qu'il y a un malentendu si 'DoMethod' est actuellement une méthode sur Base, Level1, Level2? Pouvez-vous clarifier? –

+0

@Will P Droit, désolé; Je suis en train de traiter les objets en externe, donc, non, les méthodes DoMethod ne sont pas des membres de ces classes. –

Répondre

6

Les surcharges sont résolues à lors de la compilation - et vous n'avez pas de méthode DoMethod(Base item) - donc il ne peut pas résoudre l'appel. Quitter la liste et la boucle de choses, vous écrivez efficacement:

Base o = GetBaseFromSomewhere(); 
DoMethod(o); 

Le compilateur doit trouver une méthode appelée DoMethod qui est applicable pour un seul argument de type Base. Il n'y a pas de telle méthode, d'où l'échec.

Il y a quelques options:

  • Comme le dit Markos, vous pouvez utiliser le typage dynamique en C# 4 pour faire le compilateur C# appliquent une surcharge à exécution temps en utilisant le type réel d'objet o fait référence à.
  • Vous pouvez utiliser le Visitor Pattern pour obtenir efficacement à double expédition (je ne suis jamais vraiment aime ça)
  • Vous pouvez utiliser as ou is:

    Level1 x = o as Level2; 
    if (x != null) 
    { 
        DoMethod(x); // Resolves to DoMethod(Level1) 
    } 
    else 
    { 
        Level2 y = o as Level2; 
        if (y != null) 
        { 
         DoMethod(y); // Resolves to DoMethod(Level2) 
        } 
    } 
    

    Encore une fois, cela est assez laid

  • Modifiez ce que vous faites pour pouvoir utiliser l'héritage normal, si possible
2

Surcharge am ethod utilise le type statique de la variable et non le type d'exécution. Vous souhaitez utiliser l'héritage et en remplaçant.

class Base { public virtual void DoMethod() { /* ... */ } } 
class Level1 : Base { public override void DoMethod() { /* ... */ } } 
class Level2 : Level1 { public override void DoMethod() { /* ... */ } } 
1

Quelle méthode est appelée est déterminée dans le temps Compile pas l'exécution, de sorte que le compilateur ne peut pas savoir qui appeler. Vous avez 2 options: Passez sur le type de l'objet et appelez la méthode appropriée, ou si vous utilisez .NET 4, utilisez le type dynamique.

foreach(dynamic o in oList) 
{ 
    DoMethod(o); 
} 
+0

L'utilisation de 'dynamic' aurait un impact sur les performances. Plus précisément, je pense que c'est un endroit pour un bon polymorphisme à l'ancienne. –

+0

@Steven: Nous ne le savons pas à coup sûr. Il peut être complètement inapproprié de mettre la logique de 'DoMethod' dans' Level1' et 'Level2'. –

+0

@Jon: Bien sûr, l'une des possibilités que j'ai considérées est que cela peut nécessiter une forme de double expédition. Pourtant, nous devrions être en mesure de le retirer sans les déclarations 'switch'. –

1

Vous n'avez pas de méthode DoMethod (élément de base). La surcharge n'est pas polymorphe.Cela se fait normalement en utilisant une méthode virtuelle:

class Base { 
    public virtual void DoMethod() {...} 
} 
class Level1 : Base { 
    public override void DoMethod() {...} 
} 
// etc.. 

foreach(Base o in oList) 
{ 
    o.DoMethod(); 
} 
1

Dans votre boucle foreach, o est de type Base et aucun des DoMethod prendre une instance surcharge Base. Si possible, vous devez déplacer DoMethod à Base et le remplacer dans les deux sous-classes:

public class Base 
{ 
    public virtual void DoMethod() { ... } 
} 
1

développiez la réponse de Marc, DoMethod devrait être une méthode virtuelle dans la base, que vous invoquez sur chaque élément de la liste.

+0

Hans et Mark m'ont battu de quelques minutes, donc je leur rends hommage. Je leur ai aussi jeté des upvotes. –

0

Je ne connais pas tous les détails, mais si c'est une situation où il n'est pas vraiment approprié d'hériter, vous pouvez utiliser des interfaces à la place. Déclarez l'interface, implémentez-la sur chacune de vos classes, puis vous pourrez lancer directement l'interface et lancer la fonction à partir de là. Mon C# est un peu fragile, mais quelque chose comme,

Interface IMethodizable 
{ 
    void DoMethod(); 
} 

class Level1 : IMethodizable { 
    void DoMethod(){ 
    //insert code here 
    } 
} 

class Level2 : IMethodizable { 
    void DoMethod(){ 
    //insert code here 
    } 
} 

Cela fonctionne particulièrement bien si la seule chose que les classes ont en commun sont cette méthode. C'est très similaire à avoir une méthode virtualisée dans la classe de base et de la surcharger. Donc, ce modèle est seulement meilleur si vous ne devriez pas hériter, ou le DoMethod devra également fonctionner sur d'autres objets ne héritant pas de base, et al.

+0

Cela fonctionnerait également avec les classes de base, mais pour le PO, cela pourrait ne pas être approprié (voir les réponses ci-dessus pour en discuter). J'ai en fait un problème similaire, et dans mon cas, ce n'est pas approprié - ma méthode dépendra de la classe appelante, donc c'est là que les implémentations devraient être. Il se peut que je doive utiliser une grosse instruction switch, mais il me semble que je devrais lire sur dynamic car j'ai la chance de coder avec C# 4. (pour moi, c'est un bon exemple de Google trouvant une question d'information et une discussion sur StackOverflow!) – winwaed

Questions connexes