【问题标题】:C#: should object variables be assigned to null?C#:应该将对象变量分配给 null 吗?
【发布时间】:2011-04-23 15:56:26
【问题描述】:

在 C# 中,是否有必要将一个对象变量分配给 null,如果你已经使用它,即使它会超出范围?

【问题讨论】:

标签: c# garbage-collection null


【解决方案1】:

不,这实际上可能很危险且容易出错(考虑到有人可能稍后尝试使用它,而没有意识到它已被设置为 null)。仅当有合理的理由将其设置为 null 时才将其设置为 null。

【讨论】:

  • 在下面查看我的答案。在 Dispose() 的实现中将成员设置为 null 有很好的理由,这可能会帮助您避免错误。
  • 我不确定容易出错是一个很好的论据。如果您稍后在没有阅读中间代码的情况下使用它,那么引用可能很容易被重新分配,或者它的状态发生了变化,从而导致您出于同样的原因出现不同的错误。阅读在编辑之前理解代码和测试是解决该问题的方法。
【解决方案2】:

IMO 更重要的是在实现 IDisposable 的对象上调用 Dispose

除此之外,将 null 分配给引用变量仅意味着您明确指示范围的结束 - 大多数情况下,它只是早期的几条指令(例如,方法体中的局部变量) - 在编译器/JIT 时代优化,运行时很可能会做同样的事情,所以你真的没有得到任何东西。在少数情况下,例如静态变量等(其范围是应用程序级别),如果您使用完变量,则应该将变量分配给 null,以便对象被垃圾收集。

【讨论】:

  • 你是说垃圾收集器会查看变量是否为空来决定是否进行垃圾收集?我认为这只是它是否还活着/在范围内。
  • 这个PowerPoint演示文稿download.microsoft.com/download/e/2/1/…,从第30张幻灯片开始,展示了JIT/GC一起做什么。这意味着空赋值特别没有意义。
  • @Damien_The_Unbeliever - 一个很好的资源!感谢那。 @Craig,我实际上是想说GC很有可能将一个对象视为垃圾,即使它显然超出范围-它只关心是否使用了ref .因此,即使实例方法正在运行,对象也很可能会被垃圾收集。看到这篇优秀的文章:blogs.msdn.com/b/oldnewthing/archive/2010/08/10/10048149.aspx
  • @Craig,继续 - 所以对于超出范围的局部变量,它没有使用空赋值。但是对于具有 AppDomain 级别范围的静态变量,对象将一直存在,直到静态变量持有引用。因此,如果您不再需要该对象,可以将其显式设置为 null。
【解决方案3】:

在把车推到湖边之前,你应该把车熄火吗?
不,这是一个常见的错误,但没有任何区别。您没有将 object 设置为 null,只是对它的一个 reference - 该对象仍在内存中,并且仍必须由垃圾收集器收集。

【讨论】:

    【解决方案4】:

    这些回答中的大多数都有正确的答案,但原因却是错误的。

    如果它是一个局部变量,该变量将在方法结束时从堆栈中掉下来,因此它指向的对象将少一个引用。如果该变量是对该对象的唯一引用,则该对象可用于 GC。

    如果您将变量设置为 null(并且许多人在方法结束时被教导这样做),那么您实际上可能会延长对象在内存中的停留时间,因为 CLR 会相信该对象可以'直到方法结束才被收集,因为它在那里看到了对对象的代码引用。但是,如果您忽略 null 的设置,CLR 可以确定在您的代码中的某个点之后不再出现对该对象的调用,即使该方法尚未完成,GC 也可以收集该对象。

    【讨论】:

      【解决方案5】:

      分配给 null 通常是个坏主意:

      1. 从概念上讲,这毫无意义。
      2. 对于许多变量,您可以有足够多的额外分配给 null,从而显着增加方法的大小。某些东西的源代码越长,阅读它所花费的脑力就越多(即使其中大部分只是可以过滤掉的东西)并且越容易发现错误。只有在使代码更易于理解时,才能使代码变得比必要的更冗长。
      3. 您的空分配可能不会被优化掉。在这种情况下,编译后的代码可能不会执行真正的解除分配,直到它实际到达那个空赋值,而在大多数情况下,一旦变量除了超出范围(有时甚至是之前)之外什么都不做可以解除分配。因此,您可能会对性能产生非常小的影响。

      我唯一一次将某些东西分配给 null 以“清除”一个将不再使用的变量,而不是因为 null 实际上是我明确想要分配的值,这是两种可能的情况之一:

      1. 它是一个可能长期存在的对象的成员,将不再被该对象使用,并且相当大。这里分配给 null 是一种优化。
      2. 它是可能长期存在的对象的成员,将不再被该对象使用,因此已被释放以释放其资源。这里分配给 null 是一个安全问题,因为发现意外使用 null 对象的情况比意外使用已处置对象的情况更容易。

      这两种情况都不适用于局部变量,只适用于成员,而且都很少见。

      【讨论】:

      • 你的第二种情况是有道理的。追踪一个空引用异常比追踪一个半处理的对象更容易
      • @rolls 是的,特别是因为使用这种陈旧对象的效果可能与错误的代码相距甚远。
      【解决方案6】:

      没有。当涉及到局部变量时,如果您对对象有引用,则根本没有区别,重要的是是否会使用该引用。

      在代码中添加一个额外的 null 赋值不会对性能造成太大影响,并且根本不会影响内存管理,但它会在代码中添加无动机的语句,使其可读性降低。

      垃圾收集器知道代码中最后一次使用引用的时间,因此它可以在不再需要对象时立即收集它。

      例子:

      {
        // Create an object
        StringBuilder b = new StringBuilder();
        b.Append("asdf");
        // Here is the last use of the object:
        string x = b.ToString();
        // From this point the object can be collected whenever the GC feels like it
        // If you assign a null reference to the variable here is irrelevant
        b = null;
      }
      

      【讨论】:

      • GC真的知道执行的是哪段代码吗?它不只是对堆栈进行(静态)分析并跟踪所有引用吗?
      • @OlivierJacot-Descombes:当垃圾回收完成时,它确实知道代码使用了哪些变量。您可以通过一个接一个地分配几个巨大的数组并分配给局部变量来测试这一点。将收集第一个数组以为后面的数组腾出空间,即使堆栈上存在引用它们的变量。
      • +1。你说的对。在阅读了 Damien_The_Unbeliever 提到的 PPT 演示文稿的第 35 页后,我必须同意你的看法。
      【解决方案7】:

      对于必须实现 IDisposable 的对象,作为一种做法,我在 IDisposable 的实现中将所有成员设置为 null。

      在很久以前,我发现这种做法极大地改善了在 Windows Mobile 上运行的 .NET Compact Framework 应用程序的内存消耗和性能。我认为与主要的 .NET Framework 相比,当时的 .NET Compact Framework 可能有一个非常简约的垃圾收​​集器实现,并且在 IDisposable 的实现中解耦对象的行为帮助 .NET Compact Framework 上的 GC 完成了它的工作.

      这种做法的另一个原因是在对对象执行 IDisposable 之后,实际上不希望任何尝试使用已处置对象上的任何成员。当然,理想情况下,您希望在某个对象尝试访问其任何功能时从已处置的对象中取出 ObjectDisposedException,但取而代之的是 NullReferenceException 总比没有异常好。您想知道代码会弄乱已释放的对象,因为玩弄已释放的非托管资源会给应用程序带来很多麻烦。

      注意:我绝对不提倡在对象上实现 IDisposable,除了将成员设置为 null 之外没有其他原因。我说的是当您出于其他原因需要实现 IDisposable 时,即您有实现 IDisposable 的成员或您的对象包装了非托管资源。

      【讨论】:

        【解决方案8】:

        我想补充一点,AFAIK 这只是 Visual Basic 单点版本的有效模式,甚至 也有一些争议。 (IIRC 仅适用于 DAO 对象。)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-07-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-05-14
          • 2019-10-22
          • 2021-01-01
          相关资源
          最近更新 更多