完成所有对象后,是否应将它们设置为 null
(VB.NET 中的 Nothing
)?
我知道在 .NET 中,必须处理实现 IDisposable
接口的任何对象实例以释放一些资源,尽管对象在处理后仍然可以是某物(因此表单中的 isDisposed
属性),所以我认为它仍然可以驻留在内存中或至少部分驻留在内存中?
我也知道,当一个对象超出范围时,它会被标记为收集准备好进行垃圾收集器的下一次传递(尽管这可能需要一些时间)。
因此,考虑到这一点,将其设置为 null
会加快系统释放内存的速度,因为它不必确定它不再在范围内,它们是否有任何不良副作用?
MSDN 文章在示例中从不这样做,目前我这样做是因为我看不到危害。然而,我遇到了各种各样的意见,所以任何评论都是有用的。
Karl 是绝对正确的,没有必要在使用后将对象设置为 null。如果一个对象实现了 IDisposable
,只需确保在完成该对象后调用 IDisposable.Dispose()
(包装在 try
..finally
或 using()
块中)。但即使您不记得调用 Dispose()
,对象上的终结器方法也应该为您调用 Dispose()
。
我认为这是一个很好的治疗方法:
深入研究 IDisposable
和这个
了解 IDisposable
尝试猜测 GC 及其管理策略没有任何意义,因为它是自我调整且不透明的。在此处与 Jeffrey Richter 在 Dot Net Rocks 上进行了很好的讨论:Jeffrey Richter on the Windows Memory Model 和 Richters 的书 CLR via C# 第 20 章有很好的处理:
完成对象后避免将对象设置为 null 的另一个原因是,它实际上可以使它们保持更长时间的活动。
例如
void foo()
{
var someType = new SomeType();
someType.DoSomething();
// someType is now eligible for garbage collection
// ... rest of method not using 'someType' ...
}
将允许 someType 引用的对象在调用“DoSomething”之后进行 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 will usually optimized away the assignment to null,所以这两个代码位最终是相同的。
GC.KeepAlive(someType);
请参阅 ericlippert.com/2013/06/10/construction-destruction
不,不要为空对象。您可以查看 https://web.archive.org/web/20160325050833/http://codebetter.com/karlseguin/2008/04/28/foundations-of-programming-pt-7-back-to-basics-memory/ 以了解更多信息,但将内容设置为 null 不会做任何事情,只会弄脏您的代码。
一般来说,使用后不需要空对象,但在某些情况下,我发现这是一个很好的做法。
如果一个对象实现了 IDisposable 并且存储在一个字段中,我认为将它设为空是很好的,只是为了避免使用已处置的对象。以下类型的错误可能会很痛苦:
this.myField.Dispose();
// ... at some later time
this.myField.DoSomething();
最好在处理完字段后将其清空,并在再次使用该字段的行处获得 NullPtrEx。否则,您可能会遇到一些神秘的错误(具体取决于 DoSomething 的作用)。
.Dispose()
的 Ctrl+F。如果您找到它,则说明您没有正确使用 IDisposable。一次性对象的唯一用途应该是在 using-block 的范围内。在 using-block 之后,您甚至无法再访问 myField
。在 using 块中,不需要设置为 null
,using-block 将为您处理对象。
Dispose
。只有在不可能的情况下(Dispose
操作所需的某些资源不再存在),对象才会抛出。 stackoverflow.com/questions/8923853/…
如果您觉得需要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
我发现使用没有任何“标题”的大括号来真正清理代码并帮助使其更易于理解。
还:
using(SomeObject object = new SomeObject())
{
// do stuff with the object
}
// the object will be disposed of
一般不需要设置为null。但是假设您的班级中有重置功能。
那么你可能会这样做,因为你不想调用 dispose 两次,因为某些 Dispose 可能没有正确实现并抛出 System.ObjectDisposed 异常。
private void Reset()
{
if(_dataset != null)
{
_dataset.Dispose();
_dataset = null;
}
//..More such member variables like oracle connection etc. _oraConnection
}
唯一应该将变量设置为 null 的情况是当变量没有超出范围并且您不再需要与其关联的数据时。否则没有必要。
这种“使用后无需将对象设置为null”并不完全准确。有时您需要在处理变量后将其设为 NULL。
是的,完成后,您应该始终调用 .Dispose()
或 .Close()
。无论是文件句柄、数据库连接还是一次性对象。
与此不同的是非常实用的 LazyLoad 模式。
假设我已经实例化了 class A
的 ObjA
。 Class A
有一个名为 PropB
的 class B
的公共属性。
在内部,PropB
使用 _B
的私有变量并默认为 null。使用 PropB.Get()
时,它会检查 _PropB
是否为空,如果是,则打开将 B
实例化为 _PropB
所需的资源。然后它返回 _PropB
。
根据我的经验,这是一个非常有用的技巧。
需要 null 的地方是,如果您以某种方式重置或更改 A,使 _PropB
的内容是 A
的先前值的子项,您将需要 Dispose AND null out _PropB
,以便 LazyLoad 可以如果代码需要,请重置以获取正确的值。
如果您只执行 _PropB.Dispose()
并且不久之后期望 LazyLoad 的 null 检查成功,则它不会为 null,并且您将看到陈旧的数据。实际上,您必须在 Dispose()
之后将其设为空才能确定。
我当然希望不是这样,但是我现在有代码在 _PropB
上的 Dispose()
之后以及在执行 Dispose 的调用函数之外(因此几乎超出范围),即私有道具仍然不为空,并且过时的数据仍然存在。
最终,已处置的财产将无效,但从我的角度来看,这是不确定的。
正如 dbkk 所暗示的那样,核心原因是父容器(ObjA
和 PropB
)将 _PropB
的实例保持在范围内,尽管 Dispose()
。
在某些情况下,空引用是有意义的。例如,当您编写一个集合(如优先级队列)时,根据您的合同,在客户端将这些对象从队列中删除后,您不应该为客户端保留这些对象。
但这种事情只在长期收藏中很重要。如果队列在创建它的函数结束后无法生存,那么它的重要性就小得多了。
总的来说,你真的不应该打扰。让编译器和 GC 做他们的工作,这样你就可以做你的了。
也可以看看这篇文章:http://www.codeproject.com/KB/cs/idisposable.aspx
在大多数情况下,将对象设置为 null 无效。唯一应该确保这样做的情况是,如果您使用的是“大对象”,它的大小大于 84K(例如位图)。
Stephen Cleary 在这篇文章中解释得很好:Should I Set Variables to Null to Assist Garbage Collection?
说:
不耐烦的简短回答 是的,如果变量是静态字段,或者您正在编写可枚举方法(使用 yield return)或异步方法(使用 async 和 await)。否则,没有。这意味着在常规方法(不可枚举和非异步)中,不要将局部变量、方法参数或实例字段设置为 null。 (即使您正在实现 IDisposable.Dispose,您仍然不应该将变量设置为 null)。
我们应该考虑的重要一点是静态字段。
静态字段始终是根对象,因此垃圾收集器始终认为它们是“活动的”。如果静态字段引用了不再需要的对象,则应将其设置为 null,以便垃圾收集器将其视为符合收集条件。如果整个进程正在关闭,将静态字段设置为 null 是没有意义的。此时整个堆都将被垃圾回收,包括所有根对象。
结论:
静态字段;就是这样。其他任何事情都是浪费时间。
我相信根据 GC 实现者的设计,您无法通过无效化来加速 GC。我敢肯定,他们希望您不要担心 GC 的运行方式/时间——将其视为无处不在的为您提供保护和监视……(低头,向天空举起拳头).. .
就个人而言,当我将变量作为一种自我文档的形式完成时,我经常将它们显式设置为 null。我不会声明、使用,然后稍后设置为 null —— 我在不再需要它们后立即为 null。我明确地说,“我和你正式结束了……走开……”
在 GC 语言中是否需要取消?不,它对 GC 有帮助吗?也许是,也许不是,不确定,按设计我真的无法控制它,不管今天这个版本或那个版本的答案如何,未来的 GC 实现可能会改变我无法控制的答案。另外,如果/当清空被优化时,如果你愿意的话,它只不过是一个花哨的评论。
我想如果它让下一个追随我脚步的可怜的傻瓜更清楚我的意图,并且如果它“可能”有时可能对 GC 有帮助,那么这对我来说是值得的。主要是它让我感觉整洁清晰,而Mongo喜欢感觉整洁和清晰。 :)
我是这样看的:编程语言的存在是为了让人们给其他人一个意图的想法,让编译器对要做什么的工作请求——编译器将该请求转换为 CPU 的不同语言(有时是几种语言)—— CPU 可能会提示您使用的语言、选项卡设置、注释、风格重点、变量名称等 - CPU 的全部内容是比特流,它告诉它哪些寄存器和操作码以及内存位置要旋转。许多用代码编写的东西并没有按照我们指定的顺序转换成 CPU 消耗的东西。我们的 C、C++、C#、Lisp、Babel、汇编程序或任何理论而不是现实,写成工作声明。你看到的不是你得到的,是的,即使是汇编语言。
我确实理解“不必要的东西”(如空行)“只不过是噪音和混乱的代码”的心态。那是我职业生涯的早期;我完全明白。在这个关头,我倾向于使代码更清晰的东西。这不像我在我的程序中添加了 50 行“噪音”——这里或那里只有几行。
任何规则都有例外。在具有易失性内存、静态内存、竞争条件、单例、“陈旧”数据的使用以及所有这些腐烂的情况下,情况不同:您需要管理自己的内存,适当地锁定和取消,因为内存不是GC'd Universe——希望每个人都明白这一点。其余时间使用 GC'd 语言是风格问题,而不是必要性或有保证的性能提升。
归根结底,请确保您了解哪些符合 GC 资格,哪些不符合;适当地锁定、处置和取消;上蜡,脱蜡;吸气,呼气;对于其他我说的:如果感觉好,就去做。您的里程可能会有所不同......因为它应该......
我认为将某些东西设置回 null 很麻烦。想象一个场景,现在设置的项目通过属性公开。现在不知何故,在处理项目后,某些代码意外地使用了此属性,您将得到一个空引用异常,这需要进行一些调查以弄清楚到底发生了什么。
我相信框架一次性将允许抛出更有意义的 ObjectDisposedException。出于这个原因,不将这些设置回 null 会更好。
某些对象假设 .dispose()
方法强制将资源从内存中删除。
不定期副业成功案例分享