I'm currently implementing the visitor pattern in C# for a lecture in the university. It works fine at the moment, but I have a question for a task we had to do. At the moment I have something like this:
public class TreeStructure<T>
{
protected TreeStructure<T> _left;
protected TreeStructure<T> _right;
public T myValue;
public TreeStructure(TreeStructure<T> left, T value ,TreeStructure<T> right)
{
this._left = left;
this._right = right;
this.myValue = value;
}
public virtual void InOrder(MyVisitor<T> visitor)
{
if(!IsEmpty(this._left))
{
_left.InOrder(visitor);
}
visitor.Visit(this);
if(!IsEmpty(this._right))
{
_right.InOrder(visitor);
}
}
protected bool IsEmpty(TreeStructure<T> node)
{
return node == null;
}
}
public class SpecialTree<T> : TreeStructure<T>
{
public SpecialTree(TreeStructure<T> left, T value, TreeStructure<T> right)
{
this._left = left;
this._right = right;
this.myValue = value;
}
public override void InOrder(MyVisitor<T> visitor)
{
if(!IsEmpty(this._left))
{
_left.InOrder(visitor);
}
visitor.Visit(this);
Console.WriteLine("Hallo");
if(!IsEmpty(this._right))
{
_right.InOrder(visitor);
}
}
}
And now we should implement some visitors. It was no problem, until we should differentiate in one visitor the type of the tree. So I have now one visitor looking like this:
public interface MyVisitor<T>
{
void Visit(TreeStructure<T> tree);
}
public class CountVisitor<T> : MyVisitor<T>
{
public int count { get; set;}
public CountVisitor()
{
count = 0;
}
public void Visit(TreeStructure<T> tree)
{
count++;
}
public void Visit(SpecialTree<T> specialTree)
{
count+=2;
}
}
So the question or the problem I have is, that this apparently works in Java, but not in C#. The Visit()
Method for SpecialTree
is never called. So I know that I can check specifically for the type of the object like here: What is the best way to differentiate between derived classes of a base class?
But can someone explain to me, why it doesn't work with overloaded methods? Or do I have a mistake here?
EDIT: Here is one test initialisation of a tree:
TreeStructure<int> tree1 = new TreeStructure<int>(new TreeStructure<int> (null,3,null),1,new SpecialTree<int>(null,2,null));
CountVisitor<int> visitor2 = new CountVisitor<int>();
tree1.InOrder(visitor2);
Console.WriteLine("Nodecount: " + visitor2.count.ToString());
This is highly dependant on the implementation of MyVisitor<T>
. In your comment you say it is an interface. Are you sure this is the whole example? The code you show in your question does not compile because the SpecialTree<T>
does not invoke its parent constructor.
If MyVisitor<T>
does not declare Visit(SpecialTree<T> specialTree)
, the interface dispatch done by InOrder
will never reach the SpecialTree
case.
Update
Considering your updated initialization code, and assuming the interface is declared as this:
interface MyVisitor<T>
{
void Visit(TreeStructure<T> treeStructure);
void Visit(SpecialTree<T> tree);
}
Your CountVisitor<T>
gives me a result of 4, as expected.
Why didn't it work?
I'll try to explain why your example did not work. The method InOrder
in the class SpecialTree
has a parameter of type MyVisitor
. When the method Visit
is called on MyVisitor
, there is no overload that takes SpecialTree
, however, there is an overload that takes TreeStructure
. C# does not consider the implementation of the interface when looking for the correct method to call. Therefore, the method that has TreeStructure
as a parameter is invoked instead of the method that accepts the SpecialTree
parameter.
What if I don't care what kind of tree structure it is?
In response to your comment, you can just create a method that will be called by all implementations of the tree:
class DoNotCareVisitor<T> : MyVisitor<T> {
void GenericVisit(TreeStructure<T> tree) {
//.. do whatever
}
public void Visit(TreeStructure<T> tree) {
GenericVisit(tree);
}
public void Visit(SpecialTree<T> tree) {
GenericVisit(treeStructure);
}
}