方法解析 - 为什么不调用重载方法

时间:2017-06-11 22:55:48

标签: c# polymorphism overloading visitor-pattern

我正在探索访客模式,下面是代码。请注意我已经知道如何解决这个问题 - 使用虚方法并使用各种重载扩展Visitor接口。我想更好地理解c#中的类型和重载决策。

在测试方法中,当调用Accept方法时,它总是调用带有参数类型"元素"的访问方法。而不是BinaryOperator。根据关于SO的其他类似问题的答案,这是因为重载方法&对象类型在编译时解析。

链接:

why-isnt-the-overloaded

runtime-type-vs-compile-time

non-virtual-method-resolution

当我检查生成的IL时,对于Accept方法,调用Visitor.Visit()的指令是" callvirt"而不是"呼叫"。是否还有其他我应该在IL中查看的内容,表明在编译时设置了重载?

此外,如果我使用反射来检查Accept方法中的对象类型,它还会打印MathVisitor和BinaryOperator。因此运行时知道正确的类型。那么为什么没有调用Visit方法的正确重载?

抽象:

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); }
    }
}

混凝土:

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;
        }
    }
}

测试:

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);

    }
}

1 个答案:

答案 0 :(得分:0)

当你打电话给Accept时,它会进入这个方法:

    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 void Visit(Element e)
    {
        Console.WriteLine("---Not Implemented--for--Element");
    }

这是因为这是实现IElementVisitor接口的唯一方法:

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

即使执行者有其他更好的'可用的方法,因为ev的类型为IElementVisitor ,可用于调用的唯一方法是Visit(Element e) - 这就是它调用的方法。