我目前正在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
答案 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