将参数作为参数传递给重载方法时,区分基类和派生类

时间:2014-10-31 10:47:57

标签: c# visitor

我目前正在C#中实施访问者模式,以便在大学进行讲座。它目前工作正常,但我有一个问题,我们必须做的任务。 目前我有这样的事情:

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

现在我们应该实施一些访客。这是没问题的,直到我们应该在一个访问者中区分树的类型。所以我现在有一个访问者看起来像这样:

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

所以问题或问题是,这显然适用于Java,但不适用于C#。永远不会调用Visit()的{​​{1}}方法。所以我知道我可以像这里一样专门检查对象的类型:What is the best way to differentiate between derived classes of a base class? 但有人可以向我解释,为什么它不适用于重载方法?或者我在这里有错误?

编辑:这是树的一个测试初始化​​:

SpecialTree

4 个答案:

答案 0 :(得分:0)

这在很大程度上取决于MyVisitor<T>的实施。在你的评论中,你说它是一个界面。你确定这就是整个例子吗?您在问题中显示的代码无法编译,因为SpecialTree<T>不会调用其父构造函数。

如果MyVisitor<T>未声明Visit(SpecialTree<T> specialTree)InOrder完成的界面调度将永远不会达到SpecialTree的情况。

<强>更新

考虑更新的初始化代码,并假设接口声明为:

interface MyVisitor<T>
{
    void Visit(TreeStructure<T> treeStructure);
    void Visit(SpecialTree<T> tree);
}

您的CountVisitor<T>给出了4的结果,正如所料。

为什么它不起作用?

我会尝试解释为什么你的例子不起作用。类InOrder中的方法SpecialTree具有类型MyVisitor的参数。在Visit上调用方法MyVisitor时,没有重载需要SpecialTree,但是,重载需要TreeStructure。在寻找正确的调用方法时,C#不考虑接口的实现。因此,调用以TreeStructure作为参数的方法,而不是接受SpecialTree参数的方法。

如果我不关心它是什么类型的树结构怎么办?

在回复您的评论时,您可以创建一个将由树的所有实现调用的方法:

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

答案 1 :(得分:0)

您的方法Visit(SpecialTree<T> specialTree)不属于您的MyVisitor<T>界面(顺便说一下 - 最好在您的界面前加上大写I,因此IMyVisitor<T>会提高可读性),所以没有理由为它调用它。

换句话说,当您调用visitor.Visit(this)时,您的访问者变量的类型为MyVisitor<T>,因此此时编译器不知道您打算在那里使用特定的类顺便说一句,你的功能可能会更好地超载。

最简单的方法 - 假设您没有故意尝试这样写(并使其工作,您需要使用运行时类型检查) - 将为您添加第二种方法界面,具有以下签名:void Visit(SpecialTree<T> specialTree)

答案 2 :(得分:0)

您需要做的就是检查public 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)
    {
      if (tree is SpecialTree<T>)
          count += 2;
      else
          count++;
    }
}

答案 3 :(得分:0)

您不需要区分这两种树类型。访问者的目的是遍历结构并以一般方式收集某种度量标准。

访客应该是这样的:

public class CountVisitor<T> : MyVisitor<T>
{
    public int count { get; set;}

    public CountVisitor()
    {
        count = 0;
    }

    public void Visit(TreeStructure<T> tree)
    {
        count++;
    }
}

那就是它。

您需要在SpecialTree<T>课程中执行以下操作:

public override void InOrder(MyVisitor<T> visitor)
{
    if(!IsEmpty(this._left))
    {
        _left.InOrder(visitor);
    }
    visitor.Visit(this);
    visitor.Visit(this);
    if(!IsEmpty(this._right))
    {
        _right.InOrder(visitor);
    }
}

所以基本上,SpecialTree<T>类中覆盖的目的是调用.Visit方法两次。

现在,当我运行您的测试代码时,我得到了:

Nodecount: 4