为什么未自动初始化未分配的局部变量?

时间:2011-10-17 17:39:06

标签: c# .net variables compiler-construction initialization

似乎无法在代码中使用未分配的局部变量或检查它们,因为编译器会发出Use of unassigned local variable错误。

为什么编译器在编译时没有对这些变量使用default(T)

即使对值类型更难做,在这种情况下,引用类型可以很容易地初始化为null,对吗?

以下是一些测试代码:

public void Test ( )
{
    int x;
    string s;

    if ( x == 5 )
        Console.WriteLine ( 5 );

    if ( s != null )
        Console.WriteLine ( "s" );
}

返回:

Use of unassigned local variable 'x'
Use of unassigned local variable 's'

更新:

对于那些声称这是不允许有充分理由的人,为什么在课堂上允许这样做?

public class C
{
    public int X;
    public string S;

    public void Print ( )
    {
        Console.WriteLine ( X );
        Console.WriteLine ( S );
    }
}

此代码编译完全正常。

为什么在课程级别而不是在方法级别上没有问题?

6 个答案:

答案 0 :(得分:6)

C#是一种“成功之坑”的语言。

这是一个设计决策,因为该语言完全能够允许您使用未明确分配的本地语言。但是,通常情况下,这些变量的使用是错误的,代码路径由于某种原因没有设置值。为避免此类错误,编译器要求在使用之前分配所有本地文件。

答案 1 :(得分:6)

我看到你已经更新了你的问题,所以我会更新我的答案。您的问题分为两部分,一部分与local variables相关,另一部分与instance variables on a class instance相关。首先,这不是一个真正的编译器设计决策,而是一个语言设计决策。

Spec第12.3.1 / 12.3.2节

本地变量

我们知道为什么你可以定义一个变量而不给它一个值。一个原因,例如:

int x;
// do stuff
x = 5;  // Wow, I can initialize it later!
Console.WriteLine(x);

标准定义了这是有效代码的原因。现在,我不是在C#设计团队,但很有道理他们为什么不会自动为你初始化代码(除了你实际没有希望它是自动初始化)。

说上面的代码是您的意图,但您忘记初始化x = 5;。如果编译器已经为你自动初始化了变量,代码就会编译,但它会像你期望的那样做。

虽然这是一个简单的例子,但这是来自语言设计者的非常好的设计决策,因为它可以避免许多令人头疼的问题,试图弄清楚为什么某些东西没有按预期工作。

作为旁注,我想不出你为什么要在没有分配内容的情况下定义代码的原因,或者使用默认值(在每种情况下),对我来说可能是一个bug,我确信编译器设计师可能已经确定了这一点。

类实例变量

班级成员由最初分配的标准定义。实际上,公平地说,在catchforeachusing语句中声明的局部变量最初是未分配的。实际上,这是一个标准问题,而不是编译器问题。

如果我试图猜测为什么这是关于类实例的实例变量的情况,我会说它与如何在堆上分配内存有关,因为那是分配类的地方。在堆上分配类时,必须初始化所有成员并在堆上分配它。在类成员而不是局部变量中执行它不仅可以,它方式完成。他们只是不能保持未分配状态。

答案 2 :(得分:5)

  

1为什么编译器不允许使用未初始化的变量?

因为防止促进良好的编程。

  

2为什么编译器允许使用未初始化的类成员?

因为无法准确跟踪此信息。

答案 3 :(得分:1)

通过将您的引用类型初始化为null的建议,而不是当前行为(错误代码导致编译时错误),当您取消引用未初始化的变量时,您将获得运行时错误。这真的是你想要的吗?

答案 4 :(得分:0)

请考虑以下代码:

void blah(IDictionary<int,int> dict)
{
  for (int i=0; i<10; i++)
  {
    if ((i & 11) != 0)
    {
        int j;
        dict.TryGetValue(i, out j);
        System.Diagnostics.Debug.Print("{0}",j);
        j++;
    }
  }
}

假设传递的TryGetValue实现的IDictionary<int,int>方法实际上从未写入j [如果用C#编写,则不可能,但如果用其他语言编写则可能]。应该期望代码打印什么?

C#标准中没有任何内容要求在代码离开j语句时保留if,但没有任何要求在循环迭代之间将其重置为零。在某些情况下,强制采取任何行动都会产生额外费用。标准只允许在TryGetValue被调用时,j可以任意保持零或它在范围内时持有的最后一个值,而不是两者之一。这种方法避免了不必要的成本,但如果允许代码在重新进入作用域和写入时间之间看到j的值(将未初始化的变量作为{{用其他语言编写的代码的{1}}参数会暴露其值可能是无意的)。

答案 5 :(得分:-1)

因为你想要什么?你希望x默认为零,我希望它是5 ...

如果他们为int(s)分配0并且所有世界都开始假设,那么它们将在某个时刻变为-1,这将打破全球许多应用程序。

我认为VB6变量中的

默认分配给某些东西,并且它看起来并不像看起来那么好。

当您使用C#或C ++时,您可以根据需要分配值,而不是编译器。