C#变量初始化问题

15

我是否可以在初始化整数变量时采用下列方式的任意一种?

int i = 0;
int i;

编译器或CLR是否将它们视为相同的东西?如果我没记错,它们应该被视为相同的东西,但我似乎找不到这篇文章了。


12
在C#编程中,_字段(Fields)_总是自动初始化为该类型的默认值,在int类型的情况下为零。字段被认为是已经赋值的;即使在明确赋值之前,也可以读取其内容。_局部变量(Locals)_则不被视为已经赋值;在读取局部变量之前,你需要对其进行赋值操作。有关详细信息,请参阅C#规范中的“明确赋值”部分。 - Eric Lippert
8个回答

15
如果变量i是实例变量,它将自动被赋值为0。如果它是方法中的局部变量,则未定义,因此您需要在使用之前为其分配一个值。
例如:
class Program
{
    static void Main(string[] args)
    {
        intTest it;

        it = new intTest();

        Console.ReadLine();
    }

    class intTest
    {
        int i;

        public intTest()
        {
            int i2;

            Console.WriteLine("i = " + i);
            Console.WriteLine("i2 = " + i2);
        }
    }
}

上述代码无法编译是因为 i2 没有被赋值。但是,通过将 i2 赋值为 0,即

int i2 = 0;

编译并运行后,将会显示它们现在都被赋值为0。


int是一个结构体而不是类。结构体有默认值,而类默认为null。 - Matthew Whited
1
@Matthew Whited,他的意思不是一个类。他是在说在“类”级别上,这实际上意味着实例变量。我进行了编辑以澄清。 - mmcdole

7

我查看了IL(使用ildasm),事实上只有在构造函数中将int设置为0才真正设置为0。

public class Class1
{
    int setToZero = 0;
    int notSet;
}

生成:

.method public hidebysig specialname rtspecialname 
        instance void  .ctor() cil managed
{
  // Code size       15 (0xf)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  ldc.i4.0
  IL_0002:  stfld      int32 ClassLibrary1.Class1::setToZero
  IL_0007:  ldarg.0
  IL_0008:  call       instance void [mscorlib]System.Object::.ctor()
  IL_000d:  nop
  IL_000e:  ret
} // end of method Class1::.ctor

但是那个类的构造函数是否可能处理未显式赋值的变量的初始化呢? - Michael Todd
10
Michael说,变量的初始值(默认为零)是由内存管理器而不是构造函数来实现的。 - Eric Lippert
非常感谢Eric Lippert的澄清。我希望我能够向参与此次讨论的一个或多个人员致谢。像往常一样,我总是带着比来时更多的知识离开这里 ;) - coson

4

是的,它基本上是同样的东西。

您可以参考Coding Horror上的文章。


1

在所有这些讨论中,值得一提的是C#中的"default"关键字。

int i;等同于int i = default(int);,也等同于int i = 0;MyClass o = default(MyClass);等同于MyClass o = null;

当使用linq方法如.SingleOrDefault()时,这尤其重要,因为您可以始终使用以下内容使您的代码更易读:

int someValue = collection.<various linq methods>.SingleOrDefault();
if (someValue == default(int))
{
  //Code for the default case
}

并且

MyClass someValue = collection.<various linq methods>.SingleOrDefault();
if (someValue == default(MyClass))
{
  //Code for the default case
}

1

这里有一些回答是有误导性的,包括“Coding Horror”网站引用的文章。

当编译器配置为优化生成的代码时,编译器将优化您的代码以删除所有“不必要”的初始化。请注意,在“发布”模式下编译时,这是默认行为。

就我个人而言,我认为始终初始化所有变量非常有用。在调试模式下,性能损失很小,在发布模式下则没有损失,但对于将来维护代码的人而言,显式设置变量的收益将是巨大的,这是更好的“用代码记录代码”的风格。我记得我的一位经验丰富的同事认为Int32的默认值是Int32.MinValue而不是0。这种混乱总是会发生在代码中隐含的事物上,对我而言,它们在大多数情况下应该被避免。


1

只有当它们是字段(类成员)时才为真。OP提到了变量,名称也暗示了这一点。 - H H

0
无论何时在C#中创建一个类型,它都会自动填充零。对于类(引用类型)而言,这等同于一个空指针。因此,在任何时候处理类时,以下内容在技术上是相同的:
MyClass class;
MyClass class2 = null;

在值类型中(任何结构体,包括int / float / double等),类型会带着零传递,因此以下操作是等效的:

int i;
int j = 0;

然而,在一个方法中,编译器会检查你是否在使用类型之前为其赋值。如果你做了以下操作,编译器会报错:

int i;
Console.WriteLine{"{0}",i);

从技术上讲,上述代码应该是没有问题的 - 但由于这是程序员常见的错误之一,编译器会特别检查未赋值的本地变量并报错。然而,这只是一个编译时的问题,而不是CLR的问题。你可以生成执行上述操作的IL代码,它也能正常运行。


0

这些仅适用于字段(类变量)的等效。当类被初始化时,字段会自动分配默认值。在方法或属性内,未赋值的变量仍然保持未赋值状态,如果您尝试访问它的值,将会导致编译器错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接