实例字段与局部变量的初始化

时间:2009-10-09 09:36:40

标签: c# .net clr

我一直想知道为什么在下面的例子中可以初始化实例字段(依赖它将具有其默认值)并访问它,而局部变量显然必须进行初始化,即使我将其初始化为默认值,无论如何......

  public class TestClass
  {
    private bool a;

    public void Do()
    {
      bool b; // That would solve the problem: = false;
      Console.WriteLine(a);
      Console.WriteLine(b); //Use of unassigned local variable 'b'
    }
  }

7 个答案:

答案 0 :(得分:29)

对于局部变量,编译器对流程有一个很好的了解 - 它可以看到变量的“读取”和变量的“写入”,并证明(在大多数情况下)第一次写入将在之前发生第一次阅读。

实例变量不是这种情况。考虑一个简单的属性 - 你怎么知道有人会在它得到它之前设置它?这使得执行合理规则基本上不可行 - 因此要么必须确保在构造函数中设置所有字段,要么允许它们具有默认值。 C#团队选择了后一种策略。

答案 1 :(得分:6)

它由C#中的Definite Assignment规则控制。必须在访问变量之前明确赋值。

  

5.3明确的作业

     

在函数成员的可执行代码中的给定位置,如果编译器可以通过特定的静态流分析(第5.3.3节)证明该变量已被自动初始化,则称该变量是明确赋值的。或者至少是一项任务的目标。

     

5.3.1最初分配的变量

     

以下类别的变量被归类为最初分配的:

     
      
  • 静态变量。

  •   
  • 类实例的实例变量。

  •   
  • 最初分配的结构变量的实例变量。

  •   
  • 数组元素。

  •   
  • 值参数。

  •   
  • 参考参数。

  •   
  • 在catch子句或foreach语句中声明的变量。

  •   
     

5.3.2最初未分配的变量

     

以下类别的变量分类为最初未分配:

     
      
  • 最初未分配的struct变量的实例变量。

  •   
  • 输出参数,包括struct实例构造函数的this变量。

  •   
  • 局部变量,但在catch子句或foreach语句中声明的变量除外。

  •   

答案 2 :(得分:2)

当为新对象实例分配一块内存时,运行时在整个块上写入零,确保新对象以已知状态启动 - 这就是为什么整数默认为0,默认为双精度为0.0,指针&安培;对象引用null,依此类推。

理论上,有可能对作为方法调用的一部分分配的帧进行堆叠。虽然开销很高,但它会彻底减慢对其他方法的调用,因此不会尝试。

答案 3 :(得分:1)

实例变量具有默认值。来自C#3.0规范:

  

5.1.2.1类

中的实例变量      

类的实例变量来了   当一个新的实例出现时   该类已创建,并且已停止   当没有引用时存在   那个实例和实例的   终结者(如果有的话)已经执行。

     

实例的初始值   类的变量是默认值   变量类型的值(第5.2节)。

     

为了明确分配   检查,实例变量是   考虑最初被分配。

答案 4 :(得分:1)

这是编译器限制。编译器试图阻止你在任何地方使用未分配的变量,这是一件好事,因为使用未初始化的变量曾经是旧C代码中常见的bug。

但是,在您触发该方法调用之前,编译器无法知道实例变量是否已初始化,因为它可以由任何其他方法设置,可以通过外部代码以任何顺序调用。

答案 5 :(得分:1)

隐式构造函数为您初始化实例变量。即使你指定了一个c'tor但是没有初始化一个字段,它也可以作为在堆上创建对象的一部分来完成。堆栈局部变量不是这样。

答案 6 :(得分:1)

这真的只是警告可以告诉你的问题。警告确实没有办法确保某些其他方法没有初始化类变量,因此它只会警告可以确定未初始化的那个。

此外,这是一个警告,而不是错误,因为使用未分配的变量(它保证为'false')在技术上没有任何错误,但是将它取消分配可能是一个逻辑错误。