在.NET中使用后将对象设置为null/nothing

是否应该将所有对象设置为null(_VB.NET中的ABC_1)?

我知道,在.NET中,必须释放实现IDisposable接口的对象的任何实例,以释放某些资源,尽管对象在被释放后仍然可以是某些东西(因此在表单中isDisposed属性),因此我假设它仍然可以驻留在内存中,或者至少部分驻留在内存中。

我还知道,当一个对象超出范围时,它会被标记为收集,以便垃圾收集器的下一次传递(尽管这可能需要时间)。

因此,考虑到这一点,将其设置为null是否会加快系统释放内存的速度,因为它不必计算出内存是否不再在范围内,以及它们是否有任何不良副作用?

MSDN文章从来没有在示例中这样做,目前我这样做,因为我不能。 看到危害。然而,我遇到了各种各样的意见,所以任何评论都是有用的。

107660 次浏览

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

否,不使对象为空。您可以查看https://web.archive.org/web/20160325050833/http://codebetter.com/karlseguin/2008/04/28/foundations-of-programming-pt-7-back-to-basics-memory/以了解更多信息,但将内容设置为NULL不会执行任何操作,只会破坏代码。

只有当变量未超出范围并且不再需要与其关联的数据时,才应将变量设置为NULL.否则就没有必要了。

还有:

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

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

但这类事情只有在长期收藏中才有意义。如果队列不能在创建它的函数结束时继续存在,那么它就不那么重要了。

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

Karl是绝对正确的,没有必要在使用后将对象设置为null.如果对象实现了IDisposable,只需确保在完成该对象后调用IDisposable.Dispose()(包装在try.finallyusing()块中)。但是,即使您不记得调用Dispose(),对象上的终结器方法也应该为您调用Dispose()

我认为这是一种很好的治疗方法:

深入研究iDisposable

还有这个

了解IDisposable

试图猜测GC及其管理策略

是没有任何意义的,因为它是自我调整和不透明的。我和Jeffrey Richter在Dot Net Rocks上就内部工作机制进行了很好的讨论:Jeffrey Richter谈Windows内存模型和 里希特的书CLR通过C#第20章有一个伟大的治疗:

一般来说,没有必要在使用后清空对象,但在某些情况下,我发现这是一种很好的做法。

如果一个对象实现了IDisposable并存储在一个字段中,我认为最好将其设为null,以避免使用已释放的对象。以下类型的错误可能会令人痛苦:

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

最好在处理该字段后将其置空,并在再次使用该字段的行中获得一个nullptrex.否则,您可能会遇到一些神秘的错误(具体取决于doSomething的功能)。

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

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

史蒂夫·特兰比所述

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

我发现使用没有任何“标题”的花括号可以真正清理代码,并有助于使其更易于理解。

另一个避免在使用完对象后将其设置为null的原因是,这实际上可以使它们存活更长时间。

例如:

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


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

将允许SomeType引用的对象在调用“ DoSomething ” But之后被GC

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通常会将赋值优化为NULL,因此代码的两个位最终是相同的。

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

在大多数情况下,将对象设置为null没有任何效果。只有在处理大于84K的“大型对象”(如位图)时,才应确保这样做。

这种“使用后不需要将对象设置为null ”的说法并不完全准确。有时,您需要在释放变量后将其清空。

是的,当你完成时,你应该总是打电话给.Dispose().Close()来处理任何有它的事情。可以是文件句柄、数据库连接或一次性对象。

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

假设我有并实例化了_ABCclass A的_0。Class A有一个名为_ABCclass B的_3的公共属性。

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

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

需要NULL的原因是,如果您以某种方式重置或更改_PropB的内容是A的先前值的子级,则您将需要释放并清空_PropB,以便LazyLoad可以重置以获取正确的值(如果代码需要它)。

如果您只在0_执行_ABC,并且在此之后不久期望LazyLoad的NULL检查成功,那么它不会是NULL,并且您将看到陈旧的数据。实际上,您必须在Dispose()之后将其清零,以确保万无一失。

我当然希望它不是这样,但我现在得到的代码在_PropB上的Dispose()之后表现出这种行为,并且在执行释放的调用函数之外(因此几乎超出范围),私有属性仍然不为空,并且陈旧数据仍然存在。

最终,已释放的属性将无效,但从我的角度来看,这是不确定的。

正如dbkk所暗示的,核心原因是父容器(ObjAPropB)将_PropB的实例保持在作用域内,而不管Dispose()

通常不需要设置为NULL.但是假设您的类中有一个重置功能。

那么您可能会这样做,因为您不想调用Dispose两次,因为某些Dispose可能未正确实现并引发System.ObjectDisposed异常。

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

我相信通过GC实现器的设计,您不能_带无效的ABC_0 GC.我敢肯定,他们希望您不要担心GC如何/何时运行--把它当作无处不在的存在来保护和监视您。(低头鞠躬,向天空举起拳头)。

就我个人而言,当我使用完变量时,我经常显式地将它们设置为null,作为一种自我文档的形式。我不声明,使用,然后设置为NULL--我在不再需要它们之后立即将其设置为NULL.我明确地说,我和你正式结束了..走了..

在GC d语言中,无效是必要的吗?不。对GC有帮助吗?也许是,也许不是,我不确定,通过设计,我真的无法控制它,不管今天这个版本或那个版本的答案是什么,未来的GC实现可能会改变我无法控制的答案。另外,如果/当调零被优化时,它只是一个奇特的评论,如果你愿意的话。

我想,如果它能让下一个步我后尘的可怜虫更清楚地了解我的意图,如果它“可能”,有时可能会帮助GC,那么它对我来说是值得的。最重要的是,它让我感觉整洁和清晰,而Mongo喜欢整洁和清晰的感觉。

我是这样看的:编程语言的存在是为了让人们给别人一个意图的概念,让编译器给别人一个要做什么的工作请求--编译器将该请求转换为CPU的不同语言(有时是几种)--CPU(们)可以给出你使用的语言、你的制表符设置、注释、风格重点、变量名。CPU的一切都是关于位流的,位流告诉它要旋转哪些寄存器、操作码和内存位置。许多用代码编写的东西不会按照我们指定的顺序转换成CPU所消耗的东西。我们的C,C++,C#,Lisp,Babel,Assembler或其他任何东西都是理论而不是现实,是作为工作说明书编写的。你所看到的并不是你所得到的,是的,即使是在汇编语言中。

我确实理解“不必要的东西”(比如空行)“只是噪音和杂乱的代码”的心态。我完全理解。在这个节骨眼上,我倾向于使代码更清晰。这并不是说我在我的程序中添加了50行“噪音”--只是在这里或那里添加了几行。

任何规则都有例外。在具有易失性内存、静态内存、竞争条件、单例、“陈旧”数据的使用以及所有这类腐烂的场景中,情况就不同了:您需要管理自己的内存,适当地进行锁定和无效,因为内存不是GC领域的一部分--希望每个人都能理解这一点。其余时间,对于GC语言来说,这是风格问题,而不是必要性或保证性能提升的问题。

在一天结束时,确保您了解什么符合GC的条件,什么不符合。适当地锁定、处置和取消;上蜡,脱蜡;吸气,呼气;对于其他事情,我要说:如果感觉好,就去做。您的里程可能有所不同..就像它应该..

Stephen Cleary在这篇文章中解释得很好:我是否应该将变量设置为NULL以帮助垃圾收集?

说道:

简短

的回答,对于没有耐心的人来说 是,如果变量是静态字段,或者如果您正在编写可枚举方法(使用yield return)或异步方法(使用async和await)。否则,不.

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

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

我们应该考虑的重要问题是静态场

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

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

结论:

静态场;大概就是这样。其他的都是浪费时间

我认为将某些内容设置为null是很麻烦的。设想这样一个场景:被设置为NOW的项是通过属性公开的。现在,不知何故,一些代码片段意外地使用了这个属性。在项被释放后,您将得到一个空引用异常,这需要一些调查来弄清楚到底发生了什么。

我相信一次性框架将允许抛出更有意义的ObjectDisposedException.出于这个原因,不将这些设置为NULL会更好。