-1
class A {
    public virtual void M() { Console.Write("A"); }
}

class B: A {
    public override void M() { Console.Write("B"); }
}

class C: B {
    new public virtual void M() { Console.Write("C"); }
}

class D: C {
    public override void M() { Console.Write("D"); }
    static void Main() {
        D d = new D(); 
        C c = d; 
        B b = c; 
        A a = b;
        d.M(); c.M(); b.M(); a.M();
    }
}

我是这个概念的新手,因此将不胜感激。

4

2 回答 2

5

修饰符本质上创建了一个新的new同名方法,隐藏了同名的基方法;基本方法仍然存在,只是被隐藏了。您仍然可以从类外部调用它,只需将其转换为基类型即可。从文档中:

当用作修饰符时,new关键字显式隐藏从基类继承的成员。

另一方面,override关键字表示基方法将在子类中被覆盖。如果不通过覆盖的接口,就无法调用基本方法。

因此,在您的示例中,当您调用b.M()并且a.M()正在调用Mon 方法时B(从 继承和覆盖A)。另一方面,当您调用d.M()并且c.M()正在调用名为Mon的完全不同的方法时D(它是从 继承和覆盖的C)。

于 2013-05-29T22:43:57.113 回答
1

当您定义一个虚拟方法时,您允许该方法在继承树中的某个点被覆盖。例如您的示例中的后代类B可以以某种方式更改方法的逻辑。对虚拟方法的调用将使用最派生的覆盖,这取决于对象实例的具体类型,而不是保存该实例的变量的类型。

虚拟方法表 (VMT) 用于跟踪这些虚拟方法和可能已应用的任何覆盖。类型的变量A可以包含从类派生的任何类A- 在您的示例中,任何A,或. VMT 用于确定调用虚拟方法时要调用的覆盖方法。因此,在您的示例中,覆盖 method 。当您调用方法时,程序会在 VMT 中查找要调用的正确方法,然后调用该方法。BCDBMM

每个类都定义了自己的 VMT,变量将使用与其自身类型相关的 VMT。因此,类型变量将使用AVMT 作为类型A,类型B变量将使用 VMT 作为类型B等等,而不管存储在该变量中的实例的实际(具体)类如何。覆盖解析将处理实例的 VMT 以找到要执行的正确方法。

运算符声明了一个具有相同名称的new方法,但它不属于它所替换的方法的解析链。新方法可能是虚拟的,在这种情况下,它会获得自己的一组 VMT 条目,或者它可能是标准的非虚拟方法。

为了正确管理虚拟方法和覆盖,编译器必须有一些信息可以使用。由于它在编译时无法知道变量可能包含什么,因此它只有变量的类型可供参考。因此,当您在类型的变量上调用方法时A,它会像对类型所做的那样解析调用A- 通过生成代码来对 A 类型的对象执行运行时重载解析。任何类型的变量也是如此。 . 生成的代码适用于声明的类型,并在运行时解析覆盖。

因此,当您声明一个类型的变量C,然后调用方法M时,它正在寻找M在或以下定义的命名方法C。由于您使用了new修饰符,这将永远不会导致回溯到定义在中的虚拟方法A或其在中的重载B。由于C.M是虚拟的,它将使用 VMT 找到虚拟方法的正确覆盖。

所以一般来说,调用虚方法时实际调用的方法取决于实例的具体类型。new关键字使变量的类型也很重要。

上述某些内容在描述上比技术上更准确。实际的实现可能会有所不同,一如既往:)

于 2013-05-29T23:52:45.680 回答