方法解析——为什么重载的方法没有被调用
Method resolution - why is the overloaded method not called
我正在探索访问者模式,下面是代码。请注意,我已经知道如何解决这个问题——使用虚拟方法并使用各种重载扩展访问者界面。我试图更好地理解 c# 中的类型和重载解析。
在测试方法中,调用Accept方法时,总是调用参数类型为"Element"的Visit方法,而不是BinaryOperator。根据对 SO 上其他类似问题的回答,发生这种情况是因为在编译时解析了重载方法和对象类型。
链接:
why-isnt-the-overloaded
runtime-type-vs-compile-time
non-virtual-method-resolution
当我检查生成的 IL 时,对于 Accept 方法,调用 Visitor.Visit() 时的指令是 "callvirt" 而不是 "call"。在 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);
}
}
当你调用 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);
}
因此即使实现者有其他可用的 'better' 方法,由于 ev
是 IElementVisitor
类型 唯一可用的方法 它上面是 Visit(Element e)
- 这就是它调用的那个。
我正在探索访问者模式,下面是代码。请注意,我已经知道如何解决这个问题——使用虚拟方法并使用各种重载扩展访问者界面。我试图更好地理解 c# 中的类型和重载解析。
在测试方法中,调用Accept方法时,总是调用参数类型为"Element"的Visit方法,而不是BinaryOperator。根据对 SO 上其他类似问题的回答,发生这种情况是因为在编译时解析了重载方法和对象类型。
链接:
why-isnt-the-overloaded
runtime-type-vs-compile-time
non-virtual-method-resolution
当我检查生成的 IL 时,对于 Accept 方法,调用 Visitor.Visit() 时的指令是 "callvirt" 而不是 "call"。在 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);
}
}
当你调用 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);
}
因此即使实现者有其他可用的 'better' 方法,由于 ev
是 IElementVisitor
类型 唯一可用的方法 它上面是 Visit(Element e)
- 这就是它调用的那个。