当作为参数传递给重载方法时,区分基类和派生类
本文关键字:基类 派生 参数传递 方法 重载 | 更新日期: 2023-09-27 18:28:30
我目前正在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#中无效。从未调用SpecialTree
的Visit()
方法。所以我知道我可以像这里这样专门检查对象的类型:区分基类的派生类的最佳方法是什么?但有人能向我解释一下,为什么它不适用于重载方法吗?还是我这里有错?
编辑:以下是一个树的测试初始化:
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());
这在很大程度上取决于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的结果。
为什么不起作用
我会尽力解释为什么你的例子不起作用。类SpecialTree
中的方法InOrder
具有类型为MyVisitor
的参数。当在MyVisitor
上调用方法Visit
时,不存在占用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);
}
}
您的方法Visit(SpecialTree<T> specialTree)
不是MyVisitor<T>
接口的一部分(顺便说一句,在接口前面加一个大写的I
是一种很好的做法,所以IMyVisitor<T>
会提高可读性),所以没有理由调用它。
换句话说,当您调用visitor.Visit(this)
时,您的访问者变量的类型是MyVisitor<T>
,因此在这一点上,编译器不知道您打算在那里使用特定的类,顺便说一句,这可能会更好地重载您的函数。
最简单的方法是向接口添加第二个方法,该方法具有以下签名:void Visit(SpecialTree<T> specialTree)
。
您所需要做的就是检查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++;
}
}
您不需要区分这两种树类型。访问者的目的是遍历结构并以通用的方式收集某种度量。
访问者应该是这样的:
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