多态如何在C#中使用未定义的中间类型?

时间:2012-01-30 19:21:12

标签: c# polymorphism

在下面的代码中,我原本希望调用a.Generate(v)会导致调用V.Visit(A a),因为当调用Generate时,this的类型为A. Hoewever,它似乎{ {1}}被视为this

是否可以在InterA中明确实现(相同)方法并且仅在共享基类上实现预期的行为?如果是这样,怎么能实现呢?

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); } } } polymorphic

4 个答案:

答案 0 :(得分:15)

您希望调用v.Visit(this)根据运行时类型确定要调用Visit的重载 {{ 1}}和v

具有该功能的语言称为"双重虚拟调度"语言。 (或者,如果考虑两个以上的东西,他们被称为"多个虚拟派遣"语言,或"多方法"语言。)

C#不是双虚拟派遣语言;当在编译时做出调度决策时,它是单虚拟调度语言。也就是说,决定选择哪个重载是基于 receiver runtime 类型,而编译时类型参数

现在,在您的情况下,C#不使用单个虚拟调度,因为对this的调用甚至不是虚拟调用!调用Visit是虚拟调用这一事实完全无关紧要,Generate过载的事实也无关紧要。调度到Visit 非虚拟,因此调度逻辑完全基于接收器的编译时类型Visit和参数v。由于已知接收器属于this类型,并且已知参数类型为V,因此重载决策必须选择最佳匹配仅给出该信息。它无法选择InterVisit的{​​{1}}版本,因为它们比已知参数类型A 更多派生。它必须使用 less derived 形式参数类型B选择重载。

如果您希望在C#中实现双虚拟调度,有两种标准方法可以实现。首先,您可以使用Inter;使用Base,分析在运行时使用运行时类型执行。只需将接收器和参数强制转换为dynamic,编译器就会为您处理它。但这会带来显着的性能成本。

第二种标准方法是使用访客模式,您可以通过在互联网上搜索来找到它。我怀疑你的方法名称是否已经尝试实现访问者模式;这不是正确的做法。

答案 1 :(得分:2)

问题是Generate类中定义了Iter方法。因此,当调用Generate方法时,它会将引用传递给Iter

如果您要传递AB,请Generate虚拟并在AB级别覆盖它以正确传递铸造价值。

答案 2 :(得分:2)

这不是多态代码,多态代码具有动态绑定,它决定了在运行时调用哪个方法。两个对象必须具有类似的基类和虚拟方法才能执行此动态绑定。现在你的绑定是静态的,并在编译时确定。

答案 3 :(得分:1)

您对此有何期待?

public void Visit(Base b) { Trace.WriteLine(b.GetType().Name);  } 

不知何故,编译器为v.Visit(this)编译了一个版本的代码。根据传递的参数的实际tpye,它无法为每个调用实例编译不同的版本。

在多态设计中,Inter和它的每个派生类都负责做“个人”的事情。

public void Visit(Base b) { b.VisitedBy(v);  } 

其中VisitedBy()是虚函数。