今天我想出了一个有趣的问题。我注意到以下代码:
class A
{
public A()
{
Print();
}
public virtual void Print()
{
Console.WriteLine("Print in A");
}
}
class B : A
{
public B()
{
Print();
}
public override void Print()
{
Console.WriteLine("Print in B");
}
}
class Program
{
static void Main(string[] args)
{
A a = new B();
}
}
打印
Print in B
Print in B
我想知道它为什么两次打印“B中打印”。
答案 0 :(得分:11)
我想知道它为什么两次打印“B中打印”。
您在同一个对象上调用两次虚拟方法。即使在B
的构造函数中,该对象也是A
的实例,因此将调用重写的方法。 (我相信在C ++中,对于基类构造函数执行完之后,对象只会“成为”子类的一个实例,就多态性而言。)
请注意,这意味着在派生类的构造函数体有机会执行之前,将从构造函数调用的重写方法执行。这很危险。你应该几乎从不从构造函数中调用抽象或虚方法,正是出于这个原因。
编辑:请注意,如果您未在构造函数声明中使用{chain}来提供另一个构造函数调用来使用: this(...)
或: base(...)
,则它等同于使用: base()
。所以B
的构造函数相当于:
public B() : base()
{
Print();
}
有关构造函数链接的更多信息,请参阅my article on the topic。
答案 1 :(得分:2)
与C ++不同,构造函数中的虚拟调用仅限于类本身的定义,而C#的构造函数中完全遵循覆盖。这种做法不受欢迎,并且有充分的理由(link),但仍然允许:A
的构造函数调用B
提供的覆盖,产生您看到的输出。这是覆盖虚函数的正常行为。
答案 2 :(得分:2)
因为B覆盖了Print
方法。您的变量a
的类型为B
,而B的Print
方法如下所示:
public override void Print()
{
Console.WriteLine("Print in B");
}
答案 3 :(得分:2)
如果不调用基类构造函数,则将隐式调用基类的默认构造函数(MSDN)。
如果以这种方式定义了A类构造函数,则不会得到双输出:
class A
{
public A()
{
// does nothing
}
public A(object a)
{
Print();
}
}
两次打印“打印在B”的原因是在B类中重写了Print(),因此B.Print()是两个构造函数调用的。
我认为您可以强制在A构造函数中调用A.Print(),如下所示:
class A
{
public A()
{
((A)this).Print();
}
}
希望这有帮助。
答案 4 :(得分:1)
因为你有一个B的实例覆盖了Print,所以调用了重写的方法。此外,A的ctor将运行,然后是B,这就是它被打印两次的原因。