I am exploring the Visitor pattern and below is the code. Please note that i already know how to fix this - using Virtual methods and expanding the Visitor interface with various overloads. I am trying to better understand type and overload resolution in c#.
In the test method, when Accept method is called, it always calls Visit method with parameter type "Element" instead of BinaryOperator. As per answers to other similar questions on SO, this happens because the overloading method & object type are resolved at compile time.
Links:
When i check the generated IL, for Accept method, the instruction at the call for Visitor.Visit() is "callvirt" instead of "call". Is there something else i should be looking at in IL that indicates that the overload is set at compile time?
Also if i use reflection to inspect the object type in the Accept method, it prints MathVisitor and BinaryOperator. So the run time knows the correct types. Then why is the correct overload of Visit method not called?
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); }
}
}
Concrete:
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);
}
}