我不明白为什么打印以下输出。
静态类型为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"); }
}
答案 0 :(得分:1)
区别在于从每个地方调用每种方法的方式不同,归结为new
和virtual
/ override
之间的差异。
首先是理论,对两个关键字的解释都过于简单:
new
只是在派生类中定义了另一个方法,该方法与“隐藏”它的基类中的现有方法同名。根据调用方法的引用类型,在编译时选择要调用的方法(基本方法还是派生方法)。virtual
表示方法可以在派生类中具有替代实现,在这种情况下,应改用它。在这里,根据实际对象的类型在运行时进行选择。现在将其应用于您的案例。
这里对A
的所有调用都是完全相同的,因为它是虚拟的,唯一的实例是Sub
类型。动态调度会执行它的工作,这将导致您发现对Sub.B
的调用。
但是B
上的呼叫在两个地方。 print
方法中的一个直接在main
上的另一个。由于B
不是virtual
,因此它使用静态分派和其引用的编译时间类型来确定呼叫站点。 main
中的一个很容易理解为什么使用Sub.B
。 print
方法中的另一个不使用相同的引用,它们隐式使用this
指针在同一类中调用实例方法。完全等同于编写此代码:
public void print()
{
this.A();
this.B();
}
因此,对B
的调用完全取决于this
的编译时间类型,在这种情况下(如在该类中编写的),即为Base
。因此,Base.B
在这里被调用。
先前对print
的调用来自另一种变量的事实在这里是不相关的,因为这仅用于确定采用哪种print
实现(这里只有一个),但是无论如何动作是由方法本身完成的,因此不会影响其行为。