在.NET中使用后将对象设置为Null / Nothing

问题描述 投票:178回答:14

你完成它们后,你应该将所有对象设置为null(VB.NET中的Nothing)吗?

我理解在.NET中必须处理实现IDisposable接口的任何对象实例以释放一些资源,尽管该对象在处理后仍然可以是某种东西(因此形式中的isDisposed属性),所以我认为它可以仍然留在记忆中或至少部分?

我也知道当一个对象超出范围时,它会被标记为收集准备好下一次垃圾收集器的传递(虽然这可能需要时间)。

因此,考虑到这一点,将它设置为null加速系统释放内存,因为它不必解决它不再在范围内,它们是否有任何不良副作用?

MSDN文章从未在示例中执行此操作,目前我这样做是因为我无法看到它的危害。但是我遇到了各种意见,所以任何评论都是有用的。

c# .net vb.net memory-management null
14个回答
68
投票

Karl绝对正确,使用后无需将对象设置为null。如果一个对象实现了IDisposable,那么只要确保在完成该对象时调用IDisposable.Dispose()(包含在try..finallyusing()块中)。但即使你不记得调用Dispose(),对象的终结器方法也应该为你调用Dispose()

我认为这是一个很好的待遇:

Digging into IDisposable

还有这个

Understanding IDisposable

尝试再次猜测GC及其管理策略没有任何意义,因为它是自我调整和不透明的。关于Jeffrey Richter在Dot Net Rocks上的内部运作有一个很好的讨论:Jeffrey Richter on the Windows Memory Model和Richters的书CLR via C#第20章有一个很好的处理:


1
投票

在某些情况下,引用null是有意义的。例如,当您编写集合(如优先级队列)时,根据您的合同,在客户端将其从队列中删除后,您不应该为客户端保留这些对象。

但是这种事情只适用于长期存在的收藏品。如果队列不会在它创建的函数结束时存活下来,那么它就更不重要了。

总的来说,你真的不应该打扰。让编译器和GC完成他们的工作,这样你就可以做你的工作了。


1
投票

看看这篇文章:http://www.codeproject.com/KB/cs/idisposable.aspx

在大多数情况下,将对象设置为null无效。你应该确定这样做的唯一时间是你使用的是一个大于84K的“大对象”(比如位图)。


1
投票

Stephen Cleary在这篇文章中解释得非常好:Should I Set Variables to Null to Assist Garbage Collection?

说:

简短答案,对于不耐烦是,如果变量是静态字段,或者您正在编写可枚举方法(使用yield return)或异步方法(使用async和await)。否则,没有。

这意味着在常规方法(非可枚举和非异步)中,不要将局部变量,方法参数或实例字段设置为null。

(即使您正在实现IDisposable.Dispose,仍然不应将变量设置为null)。

我们应该考虑的重要事项是静态字段。

静态字段始终是根对象,因此垃圾收集器始终将它们视为“活动”。如果静态字段引用不再需要的对象,则应将其设置为null,以便垃圾收集器将其视为符合收集条件。

如果整个过程正在关闭,则将静态字段设置为null是没有意义的。整个堆将在那时被垃圾收集,包括所有根对象。

结论:

静态场;这就是它。别的什么都浪费时间。


0
投票

我相信通过GC实现器的设计,您无法通过无效来加速GC。我敢肯定他们更喜欢你不担心GC如何/何时运行 - 像对待它一样无处不在正在为你保护和监视......(低头向下,向天空举起拳头)...... 。

就个人而言,当我将变量作为一种自我文档形式完成时,我经常将变量显式设置为null。我没有声明,使用,然后设置为null - 我不再需要它后立即为null。我明确地说,“我已经正式完成了你......走了......”

在GC'd语言中是否需要取消?不。它对GC有帮助吗?也许是的,也许不是,不确定,通过设计我真的无法控制它,无论今天这个版本的答案如何,未来的GC实施可能会改变我无法控制的答案。如果/当归零被优化时,如果你愿意的话,它只是一个花哨的评论。

我想,如果它让我的意图更清楚,那个跟随我的脚步的下一个可怜的傻瓜,如果它“可能”有时可能帮助GC,那么它对我来说是值得的。大多数情况下,它让我感到整洁和清晰,而Mongo喜欢整洁和清晰。 :)

我这样看:编程语言的存在是为了让人们给别人一个意图的想法,一个编译器就可以做什么的工作请求 - 编译器将该请求转换为CPU的不同语言(有时是几个) - CPU可以提供您使用的语言,选项卡设置,注释,样式重点,变量名称等等。 - CPU的所有关于比特流的信息告诉它什么寄存器和操作码以及内存位置可以旋转。用代码编写的许多东西都不能转换成我们指定的序列中CPU所消耗的东西。我们的C,C ++,C#,Lisp,Babel,汇编程序或其他什么是理论而不是现实,写成工作陈述。你看到的不是你得到的,是的,即使是汇编语言。

我确实理解“不必要的东西”(如空白行)的心态“只不过是噪音和杂乱的代码。”那是我职业生涯的早期;我完全明白了。在这个时刻,我倾向于使代码更清晰的东西。这并不像我在我的程序中添加了50行“噪音” - 这里或那里有几行。

任何规则都有例外。在具有易失性内存,静态内存,竞争条件,单例,“陈​​旧”数据的使用以及所有那种腐烂的情况下,这是不同的:您需要管理自己的内存,锁定和取消作为apropos因为内存不属于GC'd Universe - 希望每个人都明白这一点。其余的时间使用GC语言,这是一种风格而非必要性或保证性能提升的问题。

在一天结束时,请确保您了解哪些符合GC条件,哪些不符合条件;适当地锁定,处置和取消;打蜡,打蜡;呼吸,呼气;对于我说的其他一切:如果感觉良好,那就去做吧。你的里程可能会有所不同......因为它应该......


-1
投票

一些对象假设.dispose()方法强制从内存中删除资源。


35
投票

避免在完成对象时将对象设置为null的另一个原因是它实际上可以使它们保持活动更长时间。

EG

void foo()
{
    var someType = new SomeType();
    someType.DoSomething();
    // someType is now eligible for garbage collection         

    // ... rest of method not using 'someType' ...
}

在调用“DoSomething”之后,允许someType引用的对象为GC'd

void foo()
{
    var someType = new SomeType();
    someType.DoSomething();
    // someType is NOT eligible for garbage collection yet
    // because that variable is used at the end of the method         

    // ... rest of method not using 'someType' ...
    someType = null;
}

有时可能会保持对象存活直到方法结束。 JIT will usually optimized away the assignment to null,所以两位代码最终都是一样的。


14
投票

不要没有null对象。您可以查看http://codebetter.com/blogs/karlseguin/archive/2008/04/27/foundations-of-programming-pt-7-back-to-basics-memory.aspx以获取更多信息,但将事物设置为null将不会执行任何操作,除了脏代码。


7
投票

也:

using(SomeObject object = new SomeObject()) 
{
  // do stuff with the object
}
// the object will be disposed of

7
投票

一般来说,使用后不需要空对象,但在某些情况下我发现这是一个很好的做法。

如果一个对象实现了IDisposable并存储在一个字段中,我认为将其置零是好的,只是为了避免使用被处置的对象。以下类型的错误可能会很痛苦:

this.myField.Dispose();
// ... at some later time
this.myField.DoSomething();

在处理之后将字段置零是很好的,并且在再次使用该字段的行处获得NullPtrEx。否则,你可能会遇到一些神秘的错误(具体取决于DoSomething的作用)。


7
投票

如果您觉得需要null变量,那么您的代码可能不够紧密。

有许多方法可以限制变量的范围:

正如Steve Tranby所说

using(SomeObject object = new SomeObject()) 
{
  // do stuff with the object
}
// the object will be disposed of

同样,您可以简单地使用花括号:

{
    // Declare the variable and use it
    SomeObject object = new SomeObject()
}
// The variable is no longer available

我发现使用没有任何“标题”的大括号来真正清理代码并使其更容易理解。


5
投票

您应该将变量设置为null的唯一时间是变量不超出范围并且您不再需要与其关联的数据。否则就没有必要了。


5
投票

通常不需要设置为null。但假设您的班级中有重置功能。

然后你可能会这样做,因为你不想两次调用dispose,因为某些Dispose可能无法正确实现并抛出System.ObjectDisposed异常。

private void Reset()
{
    if(_dataset != null)
    {
       _dataset.Dispose();
       _dataset = null;
    }
    //..More such member variables like oracle connection etc. _oraConnection
 }

3
投票

这种“使用后无需将对象设置为null”并不完全准确。在处理变量后,有时需要对变量进行NULL操作。

是的,当你完成任务时,你应该总是打电话给.Dispose().Close()。无论是文件句柄,数据库连接还是一次性对象。

与此分开是LazyLoad非常实用的模式。

说我有和实例化ObjAclass AClass A有一个名为PropB of class B的公共财产。

在内部,PropB使用_B的私有变量,默认为null。当使用PropB.Get()时,它会检查_PropB是否为null,如果是,则打开将B实例化为_PropB所需的资源。然后它返回_PropB

根据我的经验,这是一个非常有用的技巧。

需要null的地方是,如果你以某种方式重置或更改A,使得_PropB的内容是之前A值的子元素,则需要Dispose AND null out _PropB,以便LazyLoad可以重置以获取正确的值如果代码需要它。

如果您只执行_PropB.Dispose(),并且在期望LazyLoad的空检查成功后不久,它将不会为null,并且您将查看过时的数据。实际上,您必须在Dispose()之后将其置零以确定。

我当然希望它不是这样,但我现在有代码在Dispose()上的_PropB之后以及执行Dispose的调用函数之外(因此几乎超出范围)之后展示了这种行为,私人道具仍然不是null,过时的数据仍然存在。

最终,被处置的财产将无效,但从我的角度来看,这是不确定的。

正如dbkk暗示的那样,核心原因是父容器(ObjAPropB)保持_PropB的实例在范围内,尽管Dispose()

© www.soinside.com 2019 - 2024. All rights reserved.