C#未初始化的变量是危险的吗?

时间:2012-01-19 18:20:16

标签: c# clr

我熟悉C#规范section 5.3,它说在使用前必须分配变量。

在C和非托管C ++中,这是有意义的,因为堆栈没有被清除,并且用于指针的内存位置可能在任何地方(导致难以追踪错误)。

但我的印象是运行时不允许真正的“未分配”值。特别是未初始化的引用类型将始终具有空值,而不是先前调用方法或随机值时遗留的值。

这是正确的,还是我错误地假设这些年来检查无效?你能在C#中使用非真实的变量,或者CLR是否会处理这个变量并且总是设置了一些值。

6 个答案:

答案 0 :(得分:61)

  

我的印象是运行时不允许真正的“未分配”值。特别是未初始化的引用类型将始终具有空值,而不是先前调用方法或随机值所遗留的值。 这是正确的吗?

我注意到目前还没有人回答你的问题。

你实际问的问题的答案是“sorta”。

正如其他人所说,一些变量(数组元素,字段等)被归类为自动“初始分配”为其默认值。 (对于引用类型,它为null,对于数字类型为零,对于bools为false,对于用户定义的结构为自然递归)。

某些变量未归类为初始分配;特别是局部变量最初没有分配。在使用的所有点上,编译器必须将它们归类为“明确分配”。

那么你的问题实际上是“是一个局部变量,被归类为没有明确分配实际最初分配与字段相同的方式?”问题的答案是,在实践中,运行时最初会分配所有本地人。

这有几个不错的属性。首先,您可以在调试器中观察它们在第一次分配之前处于默认状态。其次,垃圾收集器不可能被欺骗去取消引用坏指针,因为堆栈上剩下的垃圾现在被视为托管引用。等等。

运行时允许将本地的初始状态保留为可以安全存在的垃圾。但作为一个实现细节,它永远不会选择这样做。它积极地将内存归零为局部变量。

然后,在使用本地人之前必须明确分配的规则的原因是,以防止您观察本地的垃圾未初始化状态。这已经不可观察了,因为CLR会主动将本地清除为默认值,与字段和数组元素相同。 这在C#中是非法的原因是因为使用未分配的本地很可能是一个错误。我们只是将其视为非法,然后编译器会阻止您遇到这样的错误。

答案 1 :(得分:9)

据我所知,每种类型都有指定的默认值。

根据此文档,类的字段将分配默认值。

http://msdn.microsoft.com/en-us/library/aa645756(v=vs.71).aspx

本文档说明以下内容始终自动分配默认值。

  • 静态变量。
  • 类实例的实例变量。
  • 最初分配的结构变量的实例变量。
  • 数组元素。
  • 价值参数。
  • 参考参数。
  • 在catch子句或foreach语句中声明的变量。

http://msdn.microsoft.com/en-us/library/aa691173(v=vs.71).aspx

此处有关实际默认值的更多信息: http://msdn.microsoft.com/en-us/library/83fhsxwc.aspx

答案 2 :(得分:3)

取决于声明变量的位置。使用默认值自动初始化类中声明的变量。

object o;
void Method()
{
    if (o == null)
    {
        // this will execute
    }
}

方法中声明的变量未初始化,但是当首次使用该变量时,编译器会检查以确保它已初始化,因此代码将无法编译。

void Method()
{
    object o;
    if (o == null) // compile error on this line
    {
    }
}

答案 3 :(得分:2)

  

特别是未初始化的引用类型将始终具有空值

我认为你混合了局部变量和成员变量。第5.3节具体讨论了局部变量。与默认的成员变量不同,本地永远不会默认为空值或其他任何内容:它们只需必须才能在首次读取之前进行分配。第5.3节解释了编译器用于确定是否已分配局部变量的规则。

答案 4 :(得分:1)

有三种方法可以为变量分配初始值:

  1. 默认情况下 - 如果您声明一个类变量而没有指定初始值,则会发生这种情况(例如),因此初始值为default(type),其中type是您声明的任何类型变量是。

  2. 使用初始化程序 - 当您声明具有初始值的变量时会发生这种情况,如int i = 12;

  3. 检索其值之前的任何点 - 如果您有一个没有初始值的局部变量,则会发生这种情况(例如)。编译器确保您没有可访问的代码路径,这些路径将在分配变量之前读取变量的值。

  4. 编译器在任何时候都不允许您读取尚未初始化的变量的值,因此您不必担心尝试时会发生什么。

答案 5 :(得分:-1)

所有原始数据类型都有默认值,因此无需担心它们。

所有引用类型都初始化为空值,因此如果您保留未初始化的引用类型,然后在该null引用类型上调用某个方法或属性,您将获得需要正常处理的运行时异常。

同样,如果未按如下方式初始化所有Nullable类型,则需要检查它们是否为null或默认值:

    int? num = null;
    if (num.HasValue == true)
    {
        System.Console.WriteLine("num = " + num.Value);
    }
    else
    {
        System.Console.WriteLine("num = Null");
    }

    //y is set to zero
    int y = num.GetValueOrDefault();

    // num.Value throws an InvalidOperationException if num.HasValue is false
    try
    {
        y = num.Value;
    }
    catch (System.InvalidOperationException e)
    {
        System.Console.WriteLine(e.Message);
    }

但是,如果您将所有变量保留为未初始化,因为编译器不会抱怨,您将不会得到任何complile错误,它只是您需要担心的运行时。