编译器琐事:这段代码的结果是什么

时间:2011-08-12 21:15:38

标签: c# compiler-construction behavior

我今天正在审查一些代码并遇到了一些代码(由此代码段准确描绘)...

public abstract class FlargBase{
    public FlargBase(){
        this.DoSomething();
    }

    public abstract void DoSomething();
}

public class PurpleFlarg: FlargBase{
    public PurpleFlarg()
      : base(){
    }

    public override void DoSomething(){
        // Do something here;
    }
}

编译器不提供任何错误或警告,但CodeAnalysis警告调用链包含对虚方法的调用,并可能产生意外结果。

我很好奇,因为正如我所看到的,可能会发生两件事。

  1. 创建基类的实例将调用没有定义实现的方法。我希望编译器出错,或者由于缺少实现而导致运行时抛出异常。我假设编译器提供了{} 的实现我错误地输入了原始代码;它确实包含了类中的抽象关键字。
  2. 创建派生类的实例将导致对尚未实际构造的类的方法进行调用。我原以为这会引发异常。
  3. 此代码已在生产环境中使用了几个月。它显然工作正常,没有人注意到任何奇怪的行为。

    我希望StackOverflow上令人难以置信的才能可以让我对这段代码的行为和后果有所了解。

5 个答案:

答案 0 :(得分:12)

在第一个构造函数运行之前,C#对象已完全构造并初始化为零。基础构造函数将调用虚方法的派生实现。

执行此操作被认为是不好的样式,因为派生类的构造函数尚未被调用时,派生实现可能会表现得很奇怪。但这种行为本身就是明确的。如果在派生实现中没有做任何事情,需要构造函数中的代码已经运行,那么它将起作用。

您可以映像运行时首先调用最派生的构造函数。它的第一个动作是隐式调用基础构造函数。我不确定它是否实际上是这样实现的,但是由于某些.net语言允许您在派生构造函数的任意点调用基本构造函数,我希望C#只需调用基类构造函数作为派生的第一个动作构造


此行为与C ++处理它的方式非常不同。在C ++中,派生类一个接一个地构造,并且在派生类的构造函数启动之前,对象仍然是基类的类型,并且忽略派生类的覆盖。

答案 1 :(得分:2)

您的PurpleFlarg.DoSomething()PurpleFlarg()构造函数体之前执行。

这可能会导致意外,因为一般的假设始终是构造函数是对对象进行操作的第一种方法。

以下是MSDN page,其中包含“错误”条件的示例。

答案 2 :(得分:2)

在C#中,覆盖方法总是解析为派生最多的实现。在C# spec here

的10.11.3(构造函数执行)中给出了一个示例
  

将变量初始值设定项转换为赋值语句,并且   这些赋值语句在调用之前执行   基类实例构造函数。这种排序确保了所有   实例字段之前由其变量初始化程序初始化   任何有权访问该实例的语句都会被执行。

     

给出示例

using System;

class A
{
   public A() {
      PrintFields();
   }

   public virtual void PrintFields() {}

}

class B: A
{
   int x = 1;
   int y;

   public B() {
      y = -1;
   }

   public override void PrintFields() {
      Console.WriteLine("x = {0}, y = {1}", x, y);
   }
}
     

当新的B()用于创建B的实例时,输出如下   制作:

x = 1, y = 0

答案 3 :(得分:0)

如果类包含抽象方法(DoSomething),那么该类也必须是抽象的,并且无法实例化。

答案 4 :(得分:0)

嗯,这个模式对于现实中对象的可覆盖工厂非常有用,所以像下一个代码中的那个案例在我看来完全合法且写得很好。

abstract class MyBase
{
    public object CustomObject { get; private set; }

    public MyBase()
    {
        this.CustomObject = this.CreateCustomObject();
    }

    protected abstract object CreateCustomObject();
}

class MyBaseList : MyBase
{
    protected override object CreateCustomObject()
    {
        return new List<int>();
    }
}

class MyBaseDict : MyBase
{
    protected override object CreateCustomObject()
    {
        return new Dictionary<int, int>();
    }
}