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

时间:2008-08-05 20:14:11

标签: c# .net vb.net memory-management null

完成后,是否应将所有对象设置为null(VB.NET中为Nothing)?

我理解在.NET中必须处理实现IDisposable接口的任何对象实例以释放一些资源,尽管该对象在处理之后仍然可以是某种东西(因此{{1}在表单中的属性),所以我假设它仍然可以驻留在内存中或至少部分存在?

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

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

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

15 个答案:

答案 0 :(得分:68)

Karl绝对正确,使用后无需将对象设置为null。如果某个对象实现了IDisposable,请确保在完成该对象后调用IDisposable.Dispose()(包含在try .. finally中,或者{using() 1}}块)。但即使您忘记拨打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 :(得分:35)

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

e.g。

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,因此两行代码最终都是相同的。

答案 2 :(得分:14)

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

答案 3 :(得分:7)

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

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

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

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

答案 4 :(得分: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 :(得分:7)

此外:

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

答案 6 :(得分:5)

一般不需要设置为null。但是假设你的课程中有一个重置功能。<​​/ p>

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

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

答案 7 :(得分:5)

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

答案 8 :(得分:3)

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

是的,完成后,您应始终致电.Dispose().Close()。无论是文件句柄,数据库连接还是一次性对象。

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

说我已经并ObjA class A实例化了Class APropB有一个名为class B PropB的公共属性。

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

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

如果需要null,如果您以某种方式重置或更改A A的内容是_PropB的先前值的子项,则需要Dispose AND null out _PropB.Dispose()因此,如果代码需要,LazyLoad可以重置以获取正确的值。

如果你只做Dispose()并且在期望LazyLoad的空检查成功后不久,它将不会为空,并且你将查看陈旧数据。实际上,您必须在Dispose()之后将其归零以确定。

我当然希望不是这样,但我现在有代码在_PropB ObjA之后和执行Dispose的调用函数之外显示出这种行为(因此差不多了范围),私人道具仍然不是空的,陈旧的数据仍然存在。

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

核心原因,正如dbkk所提到的那样,父容器(PropB_PropB)将Dispose()的实例保留在范围内,尽管{{1}}。< / p>

答案 9 :(得分:1)

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

但这种事情只适用于长寿藏品。如果队列不会在它创建的函数结束时存活下来,那么它的重要性要低得多。

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

答案 10 :(得分:1)

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

说:

  

急躁的简短答案   是的,如果变量是一个静态字段,或者您正在编写一个可枚举的方法(使用yield return)或一个异步方法(使用async和await)。否则,不会。

     

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

     

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

我们应该考虑的重要内容是静态字段

  

静态字段始终是根对象,因此垃圾收集器始终将它们视为“有效” 。如果静态字段引用了不再需要的对象,则应将其设置为null,以便垃圾收集器将其视为有资格进行收集的对象。

     

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

结论:

  

静态字段;就是这样。其他一切都是浪费时间

答案 11 :(得分:1)

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

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

答案 12 :(得分:0)

我认为将某些内容重新设置为null是很麻烦的。想象一个场景,其中现在设置为通过属性暴露的项目。现在以某种方式在处置该项目后,有一段代码意外地使用了此属性,您将得到一个null引用异常,需要进行一些调查才能弄清楚到底发生了什么。

我相信一次性框架将允许抛出ObjectDisposedException,这更有意义。出于这个原因,不将它们设置回null会更好。

答案 13 :(得分:0)

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

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

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

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

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

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

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

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

答案 14 :(得分:-1)

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