ビジター パターンとジェネリック メソッドを試してみたところ、C#.NET にある種の不一致が見つかりました。AFAIK C# コンパイラは、ジェネリック メソッドよりも明示的なオーバーロードを優先するため、次のコードは次のようになります。
public abstract class A
{
public abstract void Accept(Visitor v);
}
public class B : A
{
public override void Accept(Visitor v)
{ v.Visit(this); }
}
public class C : A
{
public override void Accept(Visitor v)
{ v.Visit(this); }
}
public class D : A
{
public override void Accept(Visitor v)
{ v.Visit(this); }
}
public class Visitor
{
public void Visit(B b)
{ Console.WriteLine("visiting B"); }
public void Visit(C c)
{ Console.WriteLine("visiting C"); }
public void Visit<T>(T t)
{ Console.WriteLine("visiting generic type: " + typeof(T).Name); }
}
class Program
{
static void Main()
{
A b = new B();
A c = new C();
A d = new D();
Visitor v = new Visitor();
b.Accept(v);
c.Accept(v);
d.Accept(v);
}
}
生成される出力は (予想どおり) です。
visiting B
visiting C
visiting generic type: D
ただし、この Visitor パターンの実装では、Visitor クラスを交換できません。抽象クラス VisitorBase を導入し、呼び出しをオーバーロードに転送すると、smth が生成されます。私には予想外……。
public abstract class A
{
public abstract void Accept(VisitorBase v);
}
public class B : A
{
public override void Accept(VisitorBase v)
{ v.Visit(this); }
}
public class C : A
{
public override void Accept(VisitorBase v)
{ v.Visit(this); }
}
public class D : A
{
public override void Accept(VisitorBase v)
{ v.Visit(this); }
}
public abstract class VisitorBase
{
public abstract void Visit<T>(T t);
}
public class Visitor : VisitorBase
{
protected void VisitImpl(B b)
{ Console.WriteLine("visiting B"); }
protected void VisitImpl(C c)
{ Console.WriteLine("visiting C"); }
protected void VisitImpl<T>(T t)
{ Console.WriteLine("visiting generic type: " + typeof(T).Name); }
public override void Visit<T>(T t)
{
VisitImpl(t); //forward the call to VisitorImpl<T> or its overloads
}
}
class Program
{
static void Main()
{
A b = new B();
A c = new C();
A d = new D();
VisitorBase v = new Visitor();
b.Accept(v);
c.Accept(v);
d.Accept(v);
}
}
出力は次のようになります。
visiting generic type: B
visiting generic type: C
visiting generic type: D
ジェネリック メソッドはジェネリック メソッドのみを優先しますか? 明示的なオーバーロードが呼び出されないのはなぜですか?