Suppose you have an abstract BaseClass
and some derived classes and that you need to visit a List<BaseClass>
with a Visitor
. The code would be:
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); }
}
Then you can use all this stuff in a method like:
var baseClassList = new List<BaseClass>();
//fill baseClassList somehow
var visitor = new Visitor();
foreach(var visitable in baseClassList)
visitable.Accept(visitor);
I think this is a bit too much for a simple dispatching of some operations on different derived classes.
Also, it is counter-intuitive that the chain starts from calling Accept
and not Visit
.
Also, if you add a new Derived3
class, you always have to do 2 things: a new overload in the Visitor
AND the override of the Accept
method in the new derived class. Often you forget the second point, getting a runtime error, and other annoying things.
I'm looking for some simpler and less verbose and more secure implementation of a Visitor
pattern in C#. Is it possible?
Given 2 hypotheses:
BaseClass
is abstract and there isn't a "default" behavior for BaseClass
(i.e., the Visitor
has a different specific behavior on EVERY derived)I found a solution:
class DynamicVisitor
{
public void Visit(BaseClass b)
{
Visit((dynamic)b);
}
public void Visit(Derived1 b) { /*do something*/ }
public void Visit(Derived2 b) { /*do something*/ }
}
And you use it this way:
var baseClassList = new List<BaseClass>();
//fill baseClassList somehow
var visitor = new Visitor();
foreach(var visitable in baseClassList)
visitor.Visit(visitable);
This way, you don't need any Accept
method, nor its overrides on the visited classes.
You only need to write the overloads on the Visitor
.
The overload Visit(BaseClass b)
ensures that any passed parameter derives at least from the BaseClass
, but the dynamic
cast makes the Visitor
to choose the specific overload at runtime.
Obviously, if an instance is exactly of type BaseClass
, you have an infinite recursive call of Visit(Base b)
, and that's because I supposed that BaseClass
is abstract: this way you cannot have an instance of that exact type.
I made some performance tests with this approximate load:
BaseClass
and 17 DerivedClasses
implementing an IVisitable
interface;BaseClass
to visit;ClassicVisitor
and a DynamicVisitor
both implementing an IVisitor
interface;Accept
methods call only the Visit
method and all the Visit
methods are empty, to maximize the measured difference in time.Stopwatch
in both Debug and Release mode.Result: the DynamicVisitor
is more than 10 times slower (~5/600ms against ~50ms).
So, this solution is suitable for contexts in which you have a small amount of calls to make on the visitor.