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

joc*_*oce 7 c# polymorphism

在下面的代码中,我原本期望调用a.Generate(v)会导致调用V.Visit(A a),因为当调用Generate是this类型A.Hoewever时,它看起来this像是一个Inter代替.

是否有可能具有不明确地执行(相同)的方法在两个预期的行为AB与仅在共享基类?如果是这样,怎么能实现呢?

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调用基础上,运行时类型两个 vthis.

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

C#不是双虚拟调度语言; 在编译时进行调度决策时,它是一种单虚拟调度语言.也就是说,它的过载来选择决定的基础上进行运行时类型的接收器,但编译时类型的参数.

现在,在您的情况下,C#不使用单个虚拟调度,因为调用Visit甚至不是虚拟调用!呼叫Generate是虚拟呼叫的事实完全无关紧要,而且Visit过载的事实也无关紧要.调度到Visit非实际上,所以调度逻辑被完全基于编译时的接收器的类型,v以及参数this.由于已知接收器是类型的V并且已知参数是类型的Inter,因此重载决策必须选择仅给出该信息的最佳匹配.它不能选择该版本的版本Visit,A或者B因为它们比已知的参数类型更加派生,Inter.它必须选择带有较少派生形式参数类型的重载Base.

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

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