C#

时间:2016-11-04 15:25:30

标签: c# overloading visitor visitor-pattern

假设您有一个抽象的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模式实现。有可能吗?

1 个答案:

答案 0 :(得分:2)

给出2个假设:

  1. 您可以使用C#4.0或更新版
  2. BaseClass是抽象的,并且没有&#34;默认&#34; BaseClass的行为(即Visitor在每个派生时都有不同的特定行为)
  3. 我找到了解决方案:

    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是抽象的:这样你就不能有一个确切类型的实例。

    修改

    我使用这种近似负载进行了一些性能测试:

    1. BaseClass和17 DerivedClasses实施IVisitable界面;
    2. 要访问的BaseClass个数百万个实例的列表;
    3. ClassicVisitorDynamicVisitor都实现了IVisitor界面;
    4. 所有Accept方法仅调用Visit方法,并且所有Visit方法都为空,以最大化测量的时间差异。
    5. 在调试和发布模式下使用Stopwatch测量的时间。
    6. 结果:DynamicVisitor慢了10倍(~5 / 600ms对抗~50ms)。

      因此,此解决方案适用于您对访问者进行少量调用的上下文。