6

今天我想出了一个有趣的问题。我注意到以下代码:

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 中打印”。

4

5 回答 5

11

我想知道为什么它会打印两次“在 B 中打印”。

您在同一个对象上调用了两次虚拟方法。该对象是Beven duringA的构造函数的一个实例,因此将调用被覆盖的方法。(我相信在 C++ 中,就多态性而言,对象仅在基类构造函数执行后“成为”子类的实例。)

请注意,这意味着从构造函数调用的重写方法将在派生类的构造函数体有机会执行之前执行。这是危险的。正是出于这个原因,您几乎不应该从构造函数中调用抽象或虚拟方法。

编辑:请注意,当您不提供另一个构造函数调用“链”到 using: this(...): base(...)在构造函数声明中时,它等效于 using : base()。SoB的构造函数等价于:

public B() : base()
{
    Print();
}

有关构造函数链接的更多信息,请参阅我关于该主题的文章

于 2012-09-28T17:12:23.590 回答
2

与 C++ 中构造函数中的虚函数调用仅限于类本身的定义不同,覆盖在 C# 的构造函数中得到完全尊重。这种做法不受欢迎,并且有充分的理由(链接),但仍然允许:A的构造函数调用由 提供的覆盖B,产生您看到的输出。这是被覆盖的虚函数的正常行为。

于 2012-09-28T17:12:16.597 回答
2

因为B覆盖的Print方法。你的变量a是 typeB的,B 的Print方法是这样的:

public override void Print()
{
    Console.WriteLine("Print in B");
}
于 2012-09-28T17:12:25.943 回答
2

如果您不调用基类构造函数,则将隐式调用基类的默认构造函数 ( MSDN )。

如果您以这种方式定义 A 类构造函数,您将不会获得双重输出:

class A
{
    public A()
    {
        // does nothing
    }

    public A(object a)
    {
        Print();
    }
}

它两次打印“Print in B”的原因是 Print() 在类 B 中被覆盖,因此 B.Print() 是两个构造函数都调用的。

我认为您可以强制在 A 构造函数中调用 A.Print() ,如下所示:

class A
{
    public A()
    {
        ((A)this).Print();
    }
}

希望这可以帮助。

于 2012-09-28T17:19:59.273 回答
1

因为你有一个 B 的实例,它覆盖了 Print,所以这个被覆盖的方法被调用了。此外,A 的 ctor 将运行,然后是 B 的,这就是它被打印两次的原因。

于 2012-09-28T17:12:29.390 回答