关于c#:在.NET中使用后将对象设置为Null / Nothing

关于c#:在.NET中使用后将对象设置为Null / Nothing

Setting Objects to Null/Nothing after use in .NET

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

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

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

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

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


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

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

Digging into IDisposable

还有这个

Understanding IDisposable

尝试再次猜测GC及其管理策略没有任何意义,因为它是自我调整和不透明的。关于Jeffrey Richter在Dot Net Rocks上的内部运作有一个很好的讨论:Jeffrey Richter关于Windows内存模型和
Richters通过C#第20章预订CLR有一个很好的待遇:


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

例如

1
2
3
4
5
6
7
8
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

1
2
3
4
5
6
7
8
9
10
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,因此两个代码位最终都是相同的。


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


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

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

正如Steve Tranby所说

1
2
3
4
5
using(SomeObject object = new SomeObject())
{
  // do stuff with the object
}
// the object will be disposed of

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

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

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


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

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

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

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


也:

1
2
3
4
5
using(SomeObject object = new SomeObject())
{
  // do stuff with the object
}
// the object will be disposed of


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

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

1
2
3
4
5
6
7
8
9
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 Aclass Aclass A有一个名为PropB 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的空检查成功后不久,它将不会为空,并且您将查看过时数据。实际上,您必须在Dispose()之后将其置零以

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

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

正如dbkk所暗示的那样,核心原因是父容器(ObjA with PropB)将_PropB的实例保留在范围内,尽管Dispose()


Stephen Cleary在这篇文章中解释得非常好:我应该将变量设置为空以协助垃圾收集吗?

说:

The Short Answer, for the Impatient
Yes, if the variable is a static field, or if you are writing an enumerable method (using yield return) or an asynchronous method (using async and await). Otherwise, no.

This means that in regular methods (non-enumerable and non-asynchronous), you do not set local variables, method parameters, or instance fields to null.

(Even if you’re implementing IDisposable.Dispose, you still should not set variables to null).

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

Static fields are always root objects, so they are always considered"alive" by the garbage collector. If a static field references an object that is no longer needed, it should be set to null so that the garbage collector will treat it as eligible for collection.

Setting static fields to null is meaningless if the entire process is shutting down. The entire heap is about to be garbage collected at that point, including all the root objects.

结论:

Static fields; that’s about it. Anything else is a waste of time.


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

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


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

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

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


我相信通过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条件,哪些不符合条件;适当地锁定,处置和取消;打蜡,打蜡;呼吸,呼气;对于我说的其他一切:如果感觉良好,那就去做吧。你的里程可能会有所不同......因为它应该......


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


推荐阅读