假设您有一个抽象的BaseClass
和一些派生类,并且您需要使用List<BaseClass>
访问Visitor
。代码是:
class Visitor
{
public void Visit(Derived1 visitable) { /*do something*/ }
public void Visit(Derived2 visitable) { /*do something*/ }
public void Visit(Base visitable) { thrown new InvalidOperationException(); }
//other derivatives
}
class Base
{
public virtual void Accept(Visitor visitor) { visitor.Visit(this); }
}
class Derived1 : Base
{
//override is mandatory to have Visit(Derived1 visitable) called
public override void Accept(Visitor visitor) { visitor.Visit(this); }
}
class Derived2 : Base
{
//override is mandatory to have Visit(Derived2 visitable) called
public override void Accept(Visitor visitor) { visitor.Visit(this); }
}
然后你可以在以下方法中使用所有这些东西:
var baseClassList = new List<BaseClass>();
//fill baseClassList somehow
var visitor = new Visitor();
foreach(var visitable in baseClassList)
visitable.Accept(visitor);
我认为这对于在不同的派生类上简单地调度某些操作来说有点太多了。
此外,链条从调用Accept
而不是Visit
开始是违反直觉的。
另外,如果你添加一个新的Derived3
类,你总是要做两件事:Visitor
中的新重载和新派生中的Accept
方法的重写类。通常你会忘记第二点,获得运行时错误以及其他恼人的事情。
我正在C#中寻找更简单,更简洁,更安全的Visitor
模式实现。有可能吗?
答案 0 :(得分:2)
给出2个假设:
BaseClass
是抽象的,并且没有&#34;默认&#34; BaseClass
的行为(即Visitor
在每个派生时都有不同的特定行为)我找到了解决方案:
class DynamicVisitor
{
public void Visit(BaseClass b)
{
Visit((dynamic)b);
}
public void Visit(Derived1 b) { /*do something*/ }
public void Visit(Derived2 b) { /*do something*/ }
}
你用这种方式:
var baseClassList = new List<BaseClass>();
//fill baseClassList somehow
var visitor = new Visitor();
foreach(var visitable in baseClassList)
visitor.Visit(visitable);
这样,您就不需要任何Accept
方法,也不需要覆盖已访问的类。
您只需要在Visitor
上写入重载。
重载Visit(BaseClass b)
确保任何传递的参数至少从BaseClass
派生,但dynamic
强制转换使Visitor
在运行时选择特定的重载。
显然,如果一个实例完全属于BaseClass
类型,那么你有Visit(Base b)
的无限递归调用,那是因为我认为BaseClass
是抽象的:这样你就不能有一个确切类型的实例。
我使用这种近似负载进行了一些性能测试:
BaseClass
和17 DerivedClasses
实施IVisitable
界面; BaseClass
个数百万个实例的列表; ClassicVisitor
和DynamicVisitor
都实现了IVisitor
界面; Accept
方法仅调用Visit
方法,并且所有Visit
方法都为空,以最大化测量的时间差异。Stopwatch
测量的时间。 结果:DynamicVisitor
慢了10倍(~5 / 600ms对抗~50ms)。
因此,此解决方案适用于您对访问者进行少量调用的上下文。