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

时间:2019-01-30 19:30:33

标签: c# dispatch

我不明白为什么打印以下输出。

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

  • Sub1.A
  • Base.B

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

  • Sub1.A
  • Base.B

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

静态类型为Sub,调用B()会导致控制台输出:

  • Sub1.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"); }
}

1 个答案:

答案 0 :(得分:1)

区别在于从每个地方调用每种方法的方式不同,归结为newvirtual / override之间的差异。

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

  • new只是在派生类中定义了另一个方法,该方法与“隐藏”它的基类中的现有方法同名。根据调用方法的引用类型,在编译时选择要调用的方法(基本方法还是派生方法)
  • virtual表示方法可以在派生类中具有替代实现,在这种情况下,应改用它。在这里,根据实际对象的类型在运行时进行选择。

现在将其应用于您的案例。 这里对A的所有调用都是完全相同的,因为它是虚拟的,唯一的实例是Sub类型。动态调度会执行它的工作,这将导致您发现对Sub.B的调用。

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

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

因此,对B的调用完全取决于this的编译时间类型,在这种情况下(如在该类中编写的),即为Base。因此,Base.B在这里被调用。

先前对print的调用来自另一种变量的事实在这里是不相关的,因为这仅用于确定采用哪种print实现(这里只有一个),但是无论如何动作是由方法本身完成的,因此不会影响其行为。