Search code examples
c#visitor-pattern

Differentiate between base and derived class when passing as parameters to overloaded methods


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

Solution

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