在下面的代码中,我原本期望调用a.Generate(v)
会导致调用V.Visit(A a)
,因为当调用Generate是this
类型A.Hoewever时,它看起来this
像是一个Inter
代替.
是否有可能具有不明确地执行(相同)的方法在两个预期的行为A
和B
与仅在共享基类?如果是这样,怎么能实现呢?
using System;
using System.Diagnostics;
namespace Test {
class Base {}
class Inter: Base {
public virtual void Generate(V v) {
// `Visit(Base b)` and not `Visit(A a)` is called when calling
// A.Generate(v). Why?
v.Visit(this);
}
}
class A: Inter {}
class B: Inter {}
class V {
public void Visit(Base b) { throw new NotSupportedException(); }
public void Visit(A a) { Trace.WriteLine("a"); }
public void Visit(B b) { Trace.WriteLine("b"); }
}
class Program {
static void Main() {
V v = new V();
A a = new A();
B b = new B();
a.Generate(v);
b.Generate(v);
}
}
}
Run Code Online (Sandbox Code Playgroud)
编辑
在答案中建议上面的代码不是多态的.我会反对.V.Visit
是多态的.
Eri*_*ert 15
你期待调用v.Visit(this)
,以确定哪些超载的Visit
调用基础上,运行时类型的两个 v
和this
.
具有该功能的语言称为"双虚拟调度"语言.(或者,如果考虑两个以上的东西,它们被称为"多个虚拟调度"语言,或"多方法"语言.)
C#不是双虚拟调度语言; 在编译时进行调度决策时,它是一种单虚拟调度语言.也就是说,它的过载来选择决定的基础上进行运行时类型的接收器,但编译时类型的参数.
现在,在您的情况下,C#不使用单个虚拟调度,因为调用Visit
甚至不是虚拟调用!呼叫Generate
是虚拟呼叫的事实完全无关紧要,而且Visit
过载的事实也无关紧要.调度到Visit
由非实际上,所以调度逻辑被完全基于编译时的接收器的类型,v
以及参数this
.由于已知接收器是类型的V
并且已知参数是类型的Inter
,因此重载决策必须选择仅给出该信息的最佳匹配.它不能选择该版本的版本Visit
,A
或者B
因为它们比已知的参数类型更加派生,Inter
.它必须选择带有较少派生形式参数类型的重载Base
.
如果您希望在C#中实现双虚拟调度,有两种标准方法可以实现.首先,你可以使用dynamic
; with dynamic
,使用运行时类型在运行时执行分析.只需将接收器和参数转换为dynamic
,编译器将为您处理它.但这会带来显着的性能成本.
第二种标准方法是使用访客模式,您可以通过在互联网上搜索它来找到它.我怀疑你的方法名称是否已经尝试实现访问者模式; 这不是正确的方法.