The pattern you are implementing is called double virtual dispatch.
A single virtual dispatch chooses which method to call on the basis of the run-time type of the receiver and the compile time type of the arguments. This is a traditional virtual dispatch:
abstract class Animal {}
class Tiger : Animal {}
class Giraffe : Animal {}
class B
{
public virtual void M(Tiger x) {}
public virtual void M(Animal x) {}
}
class D : B
{
public override void M(Tiger x) {}
public override void M(Animal x) {}
}
...
B b = whatever;
Animal a = new Tiger();
b.M(a);
Which method is called? B.M(Tiger)
and D.M(Tiger)
are not chosen; we reject them based on the compile time type of the argument, which is Animal. But we choose whether to call B.M(Animal)
or D.M(Animal)
at runtime based on whether whatever
is new B()
or new D()
.
Double virtual dispatch chooses which method to call based on the runtime types of two things. If C# supported double virtual dispatch, which it does not, then the runtime dispatch would go to B.M(Tiger)
or D.M(Tiger)
even though the compile-time type of the argument is Animal.
C# 4 does however support dynamic dispatch. If you say
dynamic b = whatever;
dynamic a = new Tiger();
b.M(a);
Then the analysis of M will be done entirely at runtime using the runtime types of b
and a
. This is significantly slower, but it does work.
Alternatively, if you want to do double virtual dispatch and get as much analysis done at compile time as possible then the standard way to do that is to implement the Visitor Pattern, which you can look up on the internet easily.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…