动态调度 - C# 中的模板方法
Dynamic Dispatch - Template Method in C#
我不明白为什么会打印出下面的输出。
静态类型是 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"); }
}
区别在于每个方法如何从每个地方调用,归结为new
和virtual
/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
实现(这里我们只有一个),但是方法本身完成的任何操作都超出了这个范围,因此不会影响它的行为。
我不明白为什么会打印出下面的输出。
静态类型是 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"); }
}
区别在于每个方法如何从每个地方调用,归结为new
和virtual
/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
实现(这里我们只有一个),但是方法本身完成的任何操作都超出了这个范围,因此不会影响它的行为。