将null赋值给类的实例和仅声明之间有什么区别?

5

我希望了解在给一个类的实例分配null值和仅声明该类之间是否有任何区别。

以一个类为例:

public class MyClass
{
    public string FirstProperty { get; set; }
    public int SecondProperty { get; set; }
}

我声明了该类的两个实例:

MyClass Instance1 = null;
MyClass Instance2;  // just declaration

Instance1Instance2之间有什么区别吗?

如果有,那么使用它们是否安全?在示例中使用“仅声明”样式(如Instance2)是一个好习惯吗?


1
前者是一个变量,它被初始化为null,后者是一个没有明确初始化的变量,因此会获得默认值,也是null。区别在于可读性,每个人都知道null是期望的和预期的。如果您有一个out参数,编译器会强制您分配某些东西,即使它是null - Tim Schmelter
例如,Resharper会建议在这种情况下删除null赋值。 - Fabio
1
是的,这两种情况是有区别的。在第二种情况下,如果该值是一个字段,它将自动分配默认值,但如果它是一个局部变量,则不会。如果代码尝试读取未赋值的局部变量,则会导致编译器错误“使用未赋值的局部变量”。 - stuartd
很多时候我在别人的代码中只看到了一个实例的声明。试图理解这样做是否正确。谢谢大家的评论。 - Serge V.
感谢大家的回答。你们的所有答案都非常有帮助。 - Serge V.
4个回答

6

然后我创建了该类的两个实例:

您并没有创建任何实例。您只是创建了两个位置,以便在必要时放置实例。您已经明确表示第一个位置没有实例,而第二个位置则没有实例。

“Instance1”和“Instance2”之间有什么区别吗?

这取决于您将代码放在哪里。

如果该代码位于“class”或“struct”中,则您已经创建了两个字段,这两个字段最初都将设置为“null”,除非构造函数执行其他操作。

如果您将该代码放在方法中(包括构造函数或属性访问器),则您将拥有两个局部变量(尽管约定使用小写字母)。第一个变量已设置为null,您可以对其进行有效的null处理(将其传递给方法[虽然它可能会抛出异常,如果它拒绝接受null)与某些内容进行比较,以确认它确实为null或确实与真正拥有实例的某个东西不同。

第二个未被分配,因此禁止在它被确定之前执行除分配任何值外的任何操作(无论是null还是实例),否则任何尝试都将导致编译错误。例如:

MyClass Instance2;
if (valueThatJustHappensToAlwaysBeTrue)
{
   Instance2 = new MyClass();
}
bool isNull = Instance2 == null; // Error! Not guaranteed to be assigned strongly enough for the compiler to know.

如果是的话,仅声明Instance2是否安全且是一个好习惯?

尽可能地,在第一次赋值(初始化)时,最好在声明附近进行声明,理想情况下应同时进行:

MyClass instance = new MyClass();

然而,如果你有几条不同的可能路径,比如:

MyClass instance;
if (boolValue)
{
   instance = new MyClass(1); // Yes, I know there's no int-taking ctor on your class, but it defeats the argument when the bare constructor is the only one available, so let's say there is.
}
else if (otherBoolValue)
{
   throw new SomeException();
}
else if (someIntValue > 42)
{
   instance = new MyClass(3);
}
else
{
   instance = new MyClass(9);
}

现在,如果一个未初始化的instance无法到达链的末端。它要么已被设置,要么异常将被抛出。如果我们认为从MyClass instance = null开始可能更加“安全”,那么我们可能会隐藏一个错误。上面的逻辑意味着为每条路径分配了一个东西,因为规则规定不能使用未经保证分配的实例,所以在这方面的错误会导致编译器错误,并且错误会很明显。如果一开始就被分配给一个“占位符”null,这样的错误不会那么立即显而易见,可能会导致一个bug。

所以,在像光秃声明这样的情况下,更好。

但是话说回来,当可能时,应该尽量避免复杂的逻辑,因此这些复杂的链应该是罕见的。在其他情况下,声明和赋值在同一点的风格意味着两者之间没有差距,因此不会出现错误。


谢谢您的回答,现在我明白何时以及为什么要使用 MyClass 实例了。 - Serge V.

5
在你的示例中,你没有创建任何实例。你声明了两个变量。
对于第一个变量,你明确地将其赋值为 null。对于第二个变量,你没有分配任何值。因此,它包含默认值,引用类型的默认值是 null。1 从技术上讲,这两个变量的值现在没有区别。但是如果你尝试使用 Instance2 的值,编译器可能会报错,因为编译器不允许使用未初始化的变量:
MyClass Instance1 = null;
MyClass Instance2;
Console.WriteLine(Instance1); // fine
Console.WriteLine(Instance2); // raises error CS0165

1正如stuartd和Kyle所评论的那样,本地变量可能根本没有被编译器初始化,这也解释了错误。你不能指望变量在分配前有任何特定值,无论是null还是MyClass的一个创建实例。字段和属性会被初始化为类型的默认值(对于引用类型来说是null)。


对于局部变量而言,未初始化的变量实际上包含 null 是否重要?编译器不允许访问未初始化的局部变量,因此它包含什么并不重要吗? - Kyle
@Kyle 对的,在看到stuartd的评论后,我不太确定本地变量是否被初始化...可能不会,否则编译器为什么会抛出那个错误...只是想用正确的措辞来编辑我的答案。 - René Vogt
@RenéVogt 看看 Jon Hanna 的回答。他有很好的解释。顺便说一下,谢谢你的回答。 - Serge V.

1
"null"关键字是一个字面量,表示一个不引用任何对象的null引用。"null"是引用类型变量的默认值。普通值类型不能为null示例:
 class Program
    {
        class MyClass
        {
            public void MyMethod() { }
        }

        static void Main(string[] args)
        {
            // Set a breakpoint here to see that mc = null.
            // However, the compiler considers it "unassigned."
            // and generates a compiler error if you try to
            // use the variable.
            // try Console.WriteLine(mc);
            // it will return error CS0165: Use of unassigned local variable `mc'
            MyClass mc;

            // Now the variable can be used, but...
            mc = null;

            // ... a method call on a null object raises 
            // a run-time NullReferenceException.
            // Uncomment the following line to see for yourself.
            // mc.MyMethod();

            // Now mc has a value.
            mc = new MyClass();

            // You can call its method.
            mc.MyMethod();

            // Set mc to null again. The object it referenced
            // is no longer accessible and can now be garbage-collected.
            mc = null;
       }

-2

它们是相同的。

事实上,如果您正在使用ReSharper,它会告诉您“= null”是多余的。

至于您的第二个问题。我不会声明很多变量而不立即对它们进行赋值。

所以如果你只是像这样声明它

MyClass Instance2;

然后设置它。

MyClass = new Instance2;

Instance2.FirstProperty = "some string";
Instance2.SecondProperty = 1;

所有这些都可以合并在一个语句中

var MyClass = new Instance2 { FirstProperty = "some string", SecondProperty = 1 };

2
这完全没有回答问题。 - InBetween
@InBetween 你怎么看?第一个问题,它们是一样的。第二个问题是仅声明变量好吗?不,在大多数情况下,你需要初始化它们。 - Fran
楼主在问MyClass instance1MyClass instance2 = null之间的区别。前者是未初始化的变量,而后者是初始化为null的变量,在编译器看来这两个东西是非常不同的。 - InBetween

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