在 Dispose() 中设置 obj = null(Nothing) 有啥意义吗?

Posted

技术标签:

【中文标题】在 Dispose() 中设置 obj = null(Nothing) 有啥意义吗?【英文标题】:Any sense to set obj = null(Nothing) in Dispose()?在 Dispose() 中设置 obj = null(Nothing) 有什么意义吗? 【发布时间】:2011-01-17 19:31:08 【问题描述】:

Dispose() 方法中将自定义对象设置为null(VB.NET 中的Nothing)有什么意义吗? 这可以防止内存泄漏还是没用?!

让我们考虑两个例子:

public class Foo : IDisposable

    private Bar bar; // standard custom .NET object

    public Foo(Bar bar) 
        this.bar = bar;
    
    public void Dispose() 
        bar = null; // any sense?
    


public class Foo : RichTextBox

    // this could be also: GDI+, TCP socket, SQl Connection, other "heavy" object
    private Bitmap backImage; 

    public Foo(Bitmap backImage) 
        this.backImage = backImage;
    

    protected override void Dispose(bool disposing) 
        if (disposing) 
            backImage = null;  // any sense?
        
    

【问题讨论】:

【参考方案1】:

Dispose() 的目的是允许清理垃圾收集器未处理的资源。对象由 GC 处理,所以在正常情况下确实不需要将引用设置为 null。

例外情况是,如果您希望调用者调用Dispose 在此之后保留实例。在这种情况下,最好将内部引用设置为 null。然而,一次性实例通常同时被处置和释放。在这些情况下,它不会有很大的不同。

【讨论】:

在我的第二个示例中,我有一个位图,建议使用 Dispose()。现在,由于位图不是由对象 Foo 创建的,而只是传入参数,所以我不能这样做。我认为至少将其设置为 Null... 您总是必须决定在哪里清理依赖项。如果您知道该位图没有在其他任何地方使用,Foo 应该调用Dispose()。否则它应该离开它并让调用者处理清理工作。将本地引用设置为 null 并没有额外的好处。当 Foo 的实例被回收时,Bitmap 的实例也会被回收,除非调用者仍然持有对它的引用。 @serhio - 如果您想在 Foo 中使用完 Bitmap 对象后立即释放它所使用的资源(没有其他人在使用它),那么 Foo.Dispose 应该调用 backImage .处置 也许这也指重对象,如 TCP 套接字、SQL 连接等?因为我只是不知道在设计我的 Foo 对象时会或不会成为我在外部使用的“重对象”,所以也许我不能调用 Dispose。 将字段设置为 null 会取消该字段的根。你可能不会在 Dispose 中对它做任何事情;但该引用植根于它的容器,直到 GC 决定它不是。将其设置为 null 可以减轻 GC 的决定并尽早取消对象的根。请参阅 Marc 的回答。【参考方案2】:

一般不需要设置为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】:

VB.NET有意义设置为 Nothing 声明的 Private WithEvents 对象。

使用Handles 关键字的处理程序将以这种方式从这些对象中删除。

【讨论】:

【参考方案4】:

在 C# 中将对象设置为 null 只是释放对该对象的引用。

因此,理论上最好在 C# 中的 Dispose-Method 中释放托管对象的引用,但这只是为了让 GC 在收集已释放的对象之前收集引用的对象。由于两者很可能在同一次运行中被收集,GC 很可能会识别出被引用的对象仅由已处置的类型引用,因此两者都可以被收集。

释放引用的需要也非常少,因为如果你的一次性类的所有公共成员都应该抛出异常,如果该类已经被释放。因此,在处理引用的方法后,无法成功访问您的引用对象。

【讨论】:

感谢 Dave,更改了有关 VB.NET 的信息 那么,设置Nothing时C#和VB.NET有什么区别呢?为了便于阅读,我在 C# 中公开了这个问题,但我真正的项目是在 VB.NET 中。 就您的目的而言,没有区别。但是 VB.NET 是一种可怕的语言。在 VB.NET 中,如果将 Dim x 设置为 integer = nothing,然后打印“x”的值,则得到 0。在 C# 中,它无法编译,因为“int”是值类型,“null”是严格参考。所以它们的行为并不完全相同。但是对于像 IDisposable 对象这样的引用类型,它们的行为方式完全相同。【参考方案5】:

我个人倾向于;有两个原因:

这意味着如果有人忘记释放Foo(可能来自某个事件),任何下游对象(在这种情况下为Bitmap)仍然可以被收集(在未来的某个时间点 - 每当 GC 感觉喜欢它); 很可能这只是非托管资源的浅层包装,但每一点都有帮助。 我真的不喜欢仅仅因为用户忘记解开一个事件而意外保留整个对象图; IDisposable 是一个方便的“几乎杀死”开关 - 为什么不分离所有可用的东西? 更重要的是,我现在可以厚颜无耻地使用此字段来检查(在方法等中)是否要处理,如果是 null,则抛出 ObjectDisposedException

【讨论】:

在对对象调用 Dispose() 后,您多久保留一次对对象的引用? @Brian - 注意“意外”和“事件”这两个词;请注意,不一定是编写使用我的组件的代码的人。我无法修复他们的代码,但我可以让我的代码表现得很好。 抱歉,如果结果有误。我不反对这种做法。我只是更喜欢没有这些冗余的稍微简单的代码。 我假设如果您无论如何都需要实现 IDisposable(需要释放某些特定资源),那么您应该这样做,因此您不妨彻底。大概你没有在任何地方实现 IDisposable 所以你可以这样做。 +1。应始终在 Dispose 方法(如果存在)中将 Events 设置为 null,以防订阅者忘记将自己脱钩。我在每个项目中都看到过这种情况,当事件没有解除挂钩时,就会出现内存泄漏。【参考方案6】:

如果您想以某种方式阻止已处置的拥有实例被重复使用,这可能是有意义的。

当您将对一次性字段的引用设置为 null 时,您可以保证不再使用这些实例。

您不会得到ObjectDisposedException 或使用拥有的已处置实例导致的任何其他无效状态(如果您不检查空值,您可能会得到NullReferenceException)。

这对您来说可能没有意义,只要所有 IDisposable 对象都具有 IsDisposed 属性和/或抛出 ObjectDisposedException(如果它们在处置后使用) - 有些可能违反此原则并将它们设置为 null可以防止发生不必要的影响。

【讨论】:

【参考方案7】:

这几乎是无用的。我相信,在旧的 COM/VB 时代设置为 NULL 会减少您的引用计数。

.NET 并非如此。当您将 bar 设置为 null 时,您不会破坏或释放任何东西。您只是将 bar 指向的引用从您的对象更改为“null”。您的对象仍然存在(尽管现在,由于没有任何东西引用它,它最终将被垃圾收集)。除了少数例外,在大多数情况下,如果您一开始就没有制作 Foo IDisposable,就会发生同样的事情。

IDisposable 的主要目的是允许您释放非托管资源,例如 TCP 套接字或 SQL 连接,或其他任何东西。这通常通过调用非托管资源提供的任何清理函数来完成,而不是通过将引用设置为“null”。

【讨论】:

好的,如果 Bar 我有一个 TCP 套接字 怎么办?将其设置为null应该没用吗?因为它是通过参数传递的,“某人”可以使用这个对象...... 是的,没用。如果你有一个 TCP 套接字,你可以通过调用套接字的 .Close() 方法来释放它。 SQL 连接也是如此。设置为“null”实际上只会更改您对正在使用的对象的引用。 -1,设置为空允许垃圾收集器在第一次通过时进行清理。 @AMissico:设置为空而不是让它超出范围?仅当它在范围内但长时间未使用时才有意义。 @AMissico:当引用超出范围时,就会超出范围。例如,在局部变量的方法末尾。【参考方案8】:

dispose() 的目的是清理非托管资源。 TCP 连接、数据库连接和其他数据库对象以及大量此类非托管资源应该由开发人员在 dispose 方法中释放。所以这真的很有意义。

【讨论】:

对于具有 GDI+ 位图和简单自定义 .NET 对象栏的两个示例?我不处理它们,导致传入参数而不是由对象创建。

以上是关于在 Dispose() 中设置 obj = null(Nothing) 有啥意义吗?的主要内容,如果未能解决你的问题,请参考以下文章

在 customadapter 中设置 setAdapter() 的空指针

在 React JS 中设置复选框值

如何在 Fortran 程序中设置内部挂钟?

如何在 Textarea 中设置最小字符数

由于在编辑器类中设置 onClick 侦听器时出现空指针异常,Android 应用程序崩溃

关于在JS中设置标签属性