2017-06-11 7 views
-1

J'explore le modèle Visitor et le code ci-dessous. S'il vous plaît noter que je sais déjà comment résoudre ce problème - en utilisant des méthodes virtuelles et en élargissant l'interface de visiteur avec diverses surcharges. J'essaie de mieux comprendre le type et la résolution de surcharge dans C#.Résolution de la méthode - pourquoi la méthode surchargée n'est pas appelée

Dans la méthode de test, lorsque la méthode Accept est appelée, elle appelle toujours la méthode Visit avec le type de paramètre "Element" au lieu de BinaryOperator. Selon les réponses à d'autres questions similaires sur SO, cela se produit parce que la méthode de surcharge & type d'objet sont résolus au moment de la compilation.

Liens:

why-isnt-the-overloaded

runtime-type-vs-compile-time

non-virtual-method-resolution

lorsque je vérifie le produit IL, pour Accept procédé, l'instruction à l'appel à Visitor.Visit() est « callvirt "au lieu de" appeler ". Y at-il quelque chose d'autre que je devrais regarder dans IL qui indique que la surcharge est définie au moment de la compilation?

De même, si j'utilise la réflexion pour inspecter le type d'objet dans la méthode Accept, il imprime MathVisitor et BinaryOperator. Donc, le temps d'exécution connaît les types corrects. Alors pourquoi la surcharge correcte de la méthode Visit n'est pas appelée?

Abstractions:

public enum MathOp 
{ 
    ADD, 
    SUBSTRACT, 
    MULTIPLY 
} 

public interface IElementVisitor 
{ 
    void Visit(Element e); 
} 

public abstract class Element 
{ 
    public string ElementValue { get; set; } 
    public void Accept(IElementVisitor ev) { 

     //Console.WriteLine("Type for Paramter is {0}",ev.GetType().Name); 
     //Console.WriteLine("Type for 'this' is {0}", this.GetType().Name); 

     ev.Visit(this); 

     //(ev as dynamic).Visit(this as dynamic); 
    } 
    public int ToNumber 
    { 
     get { return int.Parse(ElementValue); } 
    } 
} 

Béton:

class NumberLiteral:Element 
{ 
    public NumberLiteral(int number) 
    { 
     ElementValue = number.ToString(); 
    } 
} 

class BinaryOperator:Element 
{ 
    public NumberLiteral Left { get; set; } 
    public NumberLiteral Right { get; set; } 

    public MathOp MathOpType { get; set; } 

    public BinaryOperator(MathOp optype) 
    { 
     MathOpType = optype; 
    } 
} 

class MathVisitor : IElementVisitor 
{ 
    public int Result { get; private set; } 
    public void Visit(Element e) 
    { 
     Console.WriteLine("---Not Implemented--for--Element"); 
    } 

    public void Visit(NumberLiteral e) 
    { 
     Console.WriteLine("Num Lit - do nothing"); 
    } 

    public void Visit(BinaryOperator b) 
    { 
     if (b.MathOpType.Equals(MathOp.ADD)) 
     { 
      int v1 = b.Left.ToNumber; 
      int v2 = b.Right.ToNumber; 
      Result = v1 + v2; 
     } 
    } 
} 

Test:

public class TestVisitorPattern 
{ 
    public void TestMethod() 
    { 
     NumberLiteral e1 = new NumberLiteral(1); 
     NumberLiteral e2 = new NumberLiteral(2); 

     BinaryOperator b1 = new BinaryOperator(MathOp.ADD); 

     b1.Left = e1; 
     b1.Right = e2; 


     MathVisitor mv = new MathVisitor(); 
     Console.WriteLine("------------direct call---------------"); 
     mv.Visit(b1); 
     Console.WriteLine(mv.Result); 


     mv = new MathVisitor(); 
     Console.WriteLine("------------call through accept---------------"); 

     b1.Accept(mv); 
     Console.WriteLine(mv.Result); 

    } 
} 
+0

Comme indiqué dans le doublon marqué, lors de l'appel d'une méthode via une variable typée en tant qu'interface, le compilateur n'a pas assez de contexte pour choisir une surcharge de méthode à l'exception de l'objet implémenté pour cette interface. C'est la seule surcharge de méthode visible par le compilateur à ce moment-là. Voir aussi https://stackoverflow.com/questions/2038333/overloads-done-at-compile-time et https://stackoverflow.com/questions/5572880/c-sharp-method-overload-problem-with-class- derived-from-generic-abstract-class. –

+0

Notez qu'une autre solution, au lieu d'étendre l'interface, serait que l'implémenteur de l'interface effectue une vérification de type pour sélectionner la surcharge correcte. Soit explicitement avec l'opérateur 'as ', soit implicitement via' dynamic'.À mon humble avis, il serait préférable d'encombrer votre déclaration de l'interface avec un tas de types que les utilisateurs ne doivent jamais connaître, ne importe pas l'utilisation (quelque chose qui peut être important lors de l'exposition de l'interface comme un type public types spécifiques internes). –

Répondre

0

Lorsque vous appelez Accepter, il va dans cette méthode:

public void Accept(IElementVisitor ev) 
    { 

     //Console.WriteLine("Type for Paramter is {0}",ev.GetType().Name); 
     //Console.WriteLine("Type for 'this' is {0}", this.GetType().Name); 

     ev.Visit(this); 

     //(ev as dynamic).Visit(this as dynamic); 
    } 

qui appelle à son tour en:

public void Visit(Element e) 
    { 
     Console.WriteLine("---Not Implemented--for--Element"); 
    } 

C'est parce que c'est la seule méthode qui implémente l'interface IElementVisitor:

public interface IElementVisitor 
{ 
    void Visit(Element e); 
} 

Ainsi, même si le implementator a d'autres méthodes « meilleures » disponibles, puisque ev est de type IElementVisitorles seules méthodes disponibles pour appeler dessus sont Visit(Element e) - et ainsi est celui qu'il invoque.