动态调度 - C# 中的模板方法

Dynamic Dispatch - Template Method in C#

我不明白为什么会打印出下面的输出。

静态类型是 Base 并调用 print() 并导致控制台输出:

静态类型为 Sub 并调用 print() 并导致控制台输出:

为什么这里叫 Base.B 而不是 Sub.B?

静态类型是 Sub 并且调用 B() 导致控制台输出:

程序中调用了Sub上的隐藏函数B()。但是如果我用 print().

调用它就不行了
static void Main(string[] args)
{                
            Base b = new Sub();
            Sub s =  b as Sub;

            b.print(); //See first paragraph with 2 bullet points
            s.print(); //See second paragraph bullet points
            s.B(); //See third paragraph with bullet points

}

public class Base
{
        public Base() {}

        public void print()
        {
            A();
            B();
        }

        public virtual void A() { Console.WriteLine("Base.A"); }

        public void B() { Console.WriteLine("Base.B"); }
}

public class Sub : Base
{
        public Sub()  { }

        public override void A() { Console.WriteLine("Sub1.A"); }

        public new void B() { Console.WriteLine("Sub1.B"); }
}

区别在于每个方法如何从每个地方调用,归结为newvirtual/override

之间的区别

首先是理论,对两个关键字的简单解释:

  • new 只是在派生 class 中定义了另一个方法,其名称与基 class "hiding" 中的现有方法完全相同。根据用于调用方法的引用类型,在编译时选择调用什么方法(基础或派生)。
  • virtual 表示一个方法可以在派生的 class 中有一个替代实现,在这种情况下应该使用它来代替。这里的选择是在运行时根据实际对象的类型做出的。

现在根据您的情况应用它。 对 A 的所有调用在这里完全相同,因为它是虚拟的,并且周围唯一的实例是类型 Sub。动态调度完成它的工作,这会导致调用 Sub.B,如您所见。

但是 B 上的调用在两个地方。 print 方法中的一个和直接在 main 中的另一个。由于 B 不是 virtual,它使用静态分派和它的引用的 编译时间 类型来确定调用站点。来自 main 的那个很容易理解为什么它使用 Sub.B。然而,print 方法中的另一个不使用相同的引用,它们隐含地使用 this 指针调用相同 class 中的实例方法。这完全等同于这样写:

public void print()
{
    this.A();
    this.B();
}

所以对B的调用完全取决于this的编译时类型,在这种情况下是Base(正如class中所写) .所以这里调用了Base.B

先前对 print 的调用来自另一种类型的变量这一事实与此无关,因为它仅用于确定要采用的 print 实现(这里我们只有一个),但是方法本身完成的任何操作都超出了这个范围,因此不会影响它的行为。