GVKun编程网logo

正确使用IDisposable接口(ids接口表)

6

最近很多小伙伴都在问正确使用IDisposable接口和ids接口表这两个问题,那么本篇文章就来给大家详细解答一下,同时本文还将给你拓展.net–为什么我在IDisposable类中使用私有成员IDi

最近很多小伙伴都在问正确使用IDisposable接口ids接口表这两个问题,那么本篇文章就来给大家详细解答一下,同时本文还将给你拓展.net – 为什么我在IDisposable类中使用私有成员IDispos获取CA2000?、android – 何时调用dispose并清除CompositeDisposable、c# – IDisposable问题、c# – 为什么我的IDisposable对象上的Dispose()方法不可用?等相关知识,下面开始了哦!

本文目录一览:

正确使用IDisposable接口(ids接口表)

正确使用IDisposable接口(ids接口表)

通过阅读Microsoft文档,我知道IDisposable接口的“主要”用途是清理非托管资源。

对我来说,“非托管”意味着诸如数据库连接,套接字,窗口句柄之类的东西。但是,我已经看到了Dispose()实现该方法以释放 托管
资源的代码,这对我来说似乎是多余的,因为垃圾收集器应该处理为你。

例如:

public class MyCollection : IDisposable{    private List<String> _theList = new List<String>();    private Dictionary<String, Point> _theDict = new Dictionary<String, Point>();    // Die, clear it up! (free unmanaged resources)    public void Dispose()    {        _theList.clear();        _theDict.clear();        _theList = null;        _theDict = null;    }

我的问题是,这是否会使垃圾收集器释放可用内存的MyCollection速度比正常情况更快?

编辑
:到目前为止,人们已经发布了一些使用IDisposable清除数据库,连接和位图等非托管资源的良好示例。但是,假设_theList上面的代码中包含一百万个字符串,并且您
现在 想释放该内存,而不是等待垃圾回收器。上面的代码能做到吗?

答案1

小编典典

处置的重点 释放非托管资源。它需要在某个时候完成,否则它们将永远不会被清除。垃圾收集器不知道 如何
调用DeleteHandle()类型的变量,IntPtr不知道 是否 需要调用DeleteHandle()

注意 :什么是非 托管资源 ?如果您在Microsoft .NET
Framework中找到它:它是受管理的。如果您自己去逛MSDN,那它是不受管理的。您使用P / Invoke调用进入.NET
Framework中所有可用内容的舒适环境都是不受管理的-现在您要负责清理它。

您创建的对象需要公开 一些 外界可以调用的方法,以清理非托管资源。该方法可以命名为任意名称:

public void Cleanup()

要么

public void Shutdown()

但是,此方法有一个标准化名称:

public void Dispose()

甚至创建了一个接口IDisposable,其中只有一个方法:

public interface IDisposable{   void Dispose()}

因此,使您的对象公开IDisposable接口,并以此方式保证已编写了该单一方法来清理非托管资源:

public void Dispose(){   Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);}

这样就完成了。 除了可以做得更好。


如果您的对象已将250MB System.Drawing.Bitmap
(即.NET托管的Bitmap类)分配为某种帧缓冲区怎么办?当然,这是一个托管的.NET对象,垃圾收集器将释放它。但是,您是否真的要保留250MB的内存,等待垃圾收集器
最终 释放出来并释放它呢?如果有开放的数据库连接怎么办?当然,我们不希望该连接处于打开状态,而是等待GC最终确定对象。

如果用户调用了Dispose()(意味着他们不再计划使用该对象),为什么不消除那些浪费的位图和数据库连接呢?

所以现在我们将:

  • 摆脱非托管资源(因为我们必须这样做),并且
  • 摆脱托管资源(因为我们希望有所帮助)

因此,让我们更新Dispose()方法以摆脱那些托管对象:

public void Dispose(){   //Free unmanaged resources   Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);   //Free managed resources too   if (this.databaseConnection != null)   {      this.databaseConnection.Dispose();      this.databaseConnection = null;   }   if (this.frameBufferImage != null)   {      this.frameBufferImage.Dispose();      this.frameBufferImage = null;   }}

一切都很好, 但您可以做得更好


如果对方 了打电话Dispose()给您,该怎么办?然后他们将泄漏一些 不受管理的 资源!

注意: 它们不会泄漏 托管
资源,因为最终垃圾收集器将在后台线程上运行,并释放与任何未使用的对象关联的内存。这将包括您的对象以及您使用的所有托管对象(例如BitmapDbConnection)。

如果对方忘了打电话Dispose(),我们 仍然 可以保存他们的培根!我们仍然可以
它们调用此方法:当垃圾收集器最终开始释放(即完成)对象时。

注意: 垃圾收集器最终将释放所有托管对象。完成后,它将 Finalize 在对象上调用方法。GC不了解或不在乎 您的
Dispose 方法。当我们想要摆脱不受管理的东西时,这只是我们为调用的方法选择的名称。

垃圾收集器销毁我们的对象是释放那些烦人的非托管资源的 绝佳 时机。我们通过重写Finalize()方法来做到这一点。

注意: 在C#中,您不会显式覆盖该Finalize()方法。你写一个方法 看起来像 一个 C ++的析构函数
,编译器采用的是成为您的实现Finalize()方法:

~MyObject(){    //we''re being finalized (i.e. destroyed), call Dispose in case the user forgot to    Dispose(); //<--Warning: subtle bug! Keep reading!}

但是该代码中存在一个错误。您会看到,垃圾收集器在 后台线程
上运行;您不知道销毁两个对象的顺序。完全有可能在您的Dispose()代码中,您试图摆脱的 托管 对象(因为您想提供帮助)不再存在:

public void Dispose(){   //Free unmanaged resources   Win32.DestroyHandle(this.gdiCursorBitmapStreamFileHandle);   //Free managed resources too   if (this.databaseConnection != null)   {      this.databaseConnection.Dispose(); //<-- crash, GC already destroyed it      this.databaseConnection = null;   }   if (this.frameBufferImage != null)   {      this.frameBufferImage.Dispose(); //<-- crash, GC already destroyed it      this.frameBufferImage = null;   }}

因此,您需要的一种方法是Finalize()告诉Dispose()它不应 接触任何托管 资源(因为它们 可能不再存在
),同时仍要释放非托管资源。

在标准模式要做到这一点是有Finalize()Dispose()两个呼叫的 第三个 方法(!);
在其中传递布尔值说(如果您从Dispose()(而不是Finalize())调用它),这意味着释放托管资源是安全的。

内部 方法 可能会
被赋予像“CoreDispose”,或“MyInternalDispose”一些任意名称,但传统称呼它Dispose(Boolean)

protected void Dispose(Boolean disposing)

但是更有用的参数名称可能是:

protected void Dispose(Boolean itIsSafeToAlsoFreeManagedObjects){   //Free unmanaged resources   Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);   //Free managed resources too, but only if I''m being called from Dispose   //(If I''m being called from Finalize then the objects might not exist   //anymore   if (itIsSafeToAlsoFreeManagedObjects)     {          if (this.databaseConnection != null)      {         this.databaseConnection.Dispose();         this.databaseConnection = null;      }      if (this.frameBufferImage != null)      {         this.frameBufferImage.Dispose();         this.frameBufferImage = null;      }   }}

然后,您将IDisposable.Dispose()方法的实现更改为:

public void Dispose(){   Dispose(true); //I am calling you from Dispose, it''s safe}

和您的终结者可以:

~MyObject(){   Dispose(false); //I am *not* calling you from Dispose, it''s *not* safe}

注意 :如果您的对象从实现的对象派生Dispose,那么当您覆盖Dispose时,请不要忘记调用其 基本 Dispose方法:

public override void Dispose(){    try    {        Dispose(true); //true: safe to free managed resources    }    finally    {        base.Dispose();    }}

一切都很好, 但您可以做得更好


如果用户调用Dispose()您的对象,则所有内容均已清除。稍后,当垃圾收集器出现并调用Finalize时,它将Dispose再次调用。

这不仅浪费,而且如果您的对象具有从 上次 调用到您已处置的对象的垃圾引用Dispose(),您将尝试再次处置它们!

您会在我的代码中注意到,我小心删除了对已处置Dispose对象的引用,因此,我不会尝试调用垃圾对象引用。但这并不能阻止潜伏的小错误。

当用户调用时Dispose():句柄 CursorFileBitmapIconServiceHandle
被破坏。稍后当垃圾收集器运行时,它将尝试再次破坏相同的句柄。

protected void Dispose(Boolean iAmBeingCalledFromDisposeAndNotFinalize){   //Free unmanaged resources   Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle); //<--double destroy    ...}

解决此问题的方法是告诉垃圾收集器,它无需费心地完成对象的处理-
它的资源已被清理,并且不再需要任何工作。您可以通过调用做到这一点GC.SuppressFinalize()Dispose()方法:

public void Dispose(){   Dispose(true); //I am calling you from Dispose, it''s safe   GC.SuppressFinalize(this); //Hey, GC: don''t bother calling finalize later}

现在,用户已致电Dispose(),我们有了:

  • 释放非托管资源
  • 释放托管资源

GC运行终结器没有任何意义-一切都已处理完毕。

我不能使用Finalize清理非托管资源吗?

文档Object.Finalize说明:

Finalize方法用于在销毁对象之前对当前对象所拥有的非托管资源执行清理操作。

但是MSDN文档还指出IDisposable.Dispose

执行与释放,释放或重置非托管资源相关的应用程序定义的任务。

那是什么呢?我可以清理哪一个托管资源?答案是:

这是你的选择!但是选择Dispose

您当然可以将未管理的清除操作放入终结器中:

~MyObject(){   //Free unmanaged resources   Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);   //A C# destructor automatically calls the destructor of its base class.}

这样做的问题是,您不知道垃圾收集器何时会完成您的对象。您的未管理,不需要,未使用的本机资源将一直存在,直到垃圾收集器 最终
运行为止。然后它将调用您的finalizer方法;清理非托管资源。 Object.Finalize 的文档指出了这一点:

终结器执行的确切时间是不确定的。为确保确定性地释放类实例的资源,请实施 Close
方法或提供IDisposable.Dispose实现。

这是Dispose用于清理非托管资源的优点。您将了解并控制何时清理非托管资源。他们的破坏是 “确定性的”


要回答您最初的问题:为什么现在不释放内存,而不是在GC决定这样做时释放内存?我有一个面部识别软件,由于不再需要它们, 现在* 需要 摆脱530
MB的内部图像。当我们不这样做时:机器会陷入停顿状态。
*

奖励阅读

对于喜欢此答案样式的人(解释 为什么 ,因此 如何 变得显而易见),建议您阅读Don Box的Essential COM的第一章:

  • 直接链接:Pearson Publishing的第1章样本
  • 磁铁:84bf0b960936d677190a2be355858e80ef7542c0

在35页中,他解释了使用二进制对象的问题,并在您眼前发明了COM。一旦了解了COM 的 原因
,剩下的300页就显而易见了,只详细介绍了Microsoft的实现。

我认为每个曾经处理过对象或COM的程序员都应该至少阅读第一章。这是有史以来最好的解释。

额外奖金阅读

当您知道的一切都不对时,埃里克·利珀特(Eric Lippert)

因此,确实很难编写正确的终结器,而 我能给您的最佳建议是不要尝试

.net – 为什么我在IDisposable类中使用私有成员IDispos获取CA2000?

.net – 为什么我在IDisposable类中使用私有成员IDispos获取CA2000?

我有一个实现Idisposable的类,因为它有一个私有成员字段“foo”,它是Idisposable(在构造函数中初始化).我出乎意料地得到了CA2000代码分析错误,这要求我确保处理foo.但是,我在我的类的dispose()代码中有foo.dispose(),它应该处理这个问题.

我做了一些搜索,令人惊讶的是找不到答案.我究竟做错了什么?显然我遗漏了一些基本的东西.如何编写代码来克服这个问题?

我的VB代码:

Public Class Bar
    Implements Idisposable

    Private Foo As SomedisposableThing

    Public Sub New()
        Foo = New SomedisposableThing() With {.name = "hello"}
    End Sub

    '''' snip ''''

    Private disposedValue As Boolean = False        ' To detect redundant calls '

    Protected Overridable Sub dispose(ByVal disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
                If Foo IsNot nothing Then Foo.dispose()
            End If
        End If
        Me.disposedValue = True
    End Sub

    Public Sub dispose() Implements Idisposable.dispose
        dispose(True)
        GC.SuppressFinalize(Me)
    End Sub

End Class
CA2000错误不是指实现Idisposable的容器,而是使用未正确处理的本地容器.原因是您在一次性对象上使用对象初始值设定项.将生成的实际代码基本上如下
Dim temp = New Somethingdisposable()
temp.Name = "hello"
Foo = temp

此代码被Fxcop正确标记为未在所有实例中正确处理Idisposable(可能在temp.Name =“hello”行上发生异常,在这种情况下它不会被处置).

解决方法是不在此处使用对象初始值设定项并直接初始化Foo

Foo = New Somethingdisposable()
Foo.Name = "hello"

android – 何时调用dispose并清除CompositeDisposable

android – 何时调用dispose并清除CompositeDisposable

我的问题可能是How to use CompositeDisposable of RxJava 2?的重复但要求澄清一个疑问.
根据接受的答案

// Using clear will clear all, but can accept new disposable
disposables.clear(); 
// Using dispose will clear all and set isdisposed = true, so it will not accept any new disposable
disposables.dispose(); 

在我的例子中,我使用片段作为我的视图(在MVP中查看图层),在某些情况下,我将活动片段添加到backstack,实际上不会杀死Fragment但只会查看其视图.这意味着只调用onDestroyView而不是onDestroy.后来我可以回到Backstack中的同一个片段,所以只有它的视图才被重新创建.
我有一个Compositedisposable作为我的BaseFragment的成员,它持有订阅.

我的问题是,我应该每次在onDestroyView上调用Compositedisposable吗?一旦视图恢复,它可以再次获取订阅吗?并且在onDestroy上调用dispose,这样当片段本身被销毁时,不再需要使用一次性用品了吗?

如果错了,处理的正确方法是什么.当清除和处置必须被调用.?

解决方法:

您是对的,您可以保存自己在每次创建相应视图时创建新的Compositedisposable,而是将Compositedisposable视为绑定到onCreate / onDestroy生命周期方法的单个实例,并将聚合的一次性用作片段视图的一部分在onDestroyView中调用clear.

c# – IDisposable问题

c# – IDisposable问题

说我有以下几点:
public abstract class ControlLimitBase : Idisposable 
{
}

public abstract class UpperAlarmlimit : ControlLimitBase 
{
}

public class CdsUpperAlarmlimit : UpperAlarmlimit 
{
}

两个问题:

1.
当我的Idisposable会员实际上被呼叫时,我有些困惑.当CdsUpperAlarmlimit的实例超出范围时,它们会被调用吗?

2.
如何处理在CdsUpperAlarmlimit类中创建的对象?这是否也可以从Idisposable得到?

解决方法

dispose()不会自动调用 – 这取决于代码实际使用的方式.

当你专门调用dispose()时,调用dispose():

myAlarm.dispose();

2.)使用您的类型的实例在使用块的末尾调用dispose().

using(var myAlarm = new CdsUpperAlarmlimit())
{

}

使用块是一个try / finally块的语法糖,在finally块中的对象“被使用”上调用dispose().

c# – 为什么我的IDisposable对象上的Dispose()方法不可用?

c# – 为什么我的IDisposable对象上的Dispose()方法不可用?

我有一个这个领域的课:
private WcfChannelFactory<IPrestoService> _channelFactory;

在dispose()方法中,我这样做:

if (_channelFactory != null) { _channelFactory.dispose(); }

但这会产生错误:

Cannot access explicit implementation of Idisposable.dispose

做了一些research之后,似乎我可以这样处理:

if (_channelFactory != null) { (_channelFactory as Idisposable).dispose(); }

我不明白两件事:

>为什么dispose()不可用? WcfChannelFactory< T>源自ChannelFactory< T>,它源自实现Idisposable的ChannelFactory.然而ChannelFactory没有dispose()方法.这怎么可能?
>如果我可以(应该?)只是在_channelFactory上调用Close(),为什么XML文档没有声明Close()也会调用dispose()?也许不会?这令人困惑.

解决方法

>由于dispose方法对Idisposable接口实现了明确性,因此只有在引用Idisposable类型时才能看到该方法.方法就在那里,但是当你有一个不同类型的引用时,你看不到它.它类似于私有方法只能从类本身的代码中看到,尽管它始终存在. > Close方法不会为此类调用dispose.该方法不关闭工厂,它启动异步关闭过程.当Close方法退出时,关闭过程没有完成,因此此时无法处理对象.

关于正确使用IDisposable接口ids接口表的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于.net – 为什么我在IDisposable类中使用私有成员IDispos获取CA2000?、android – 何时调用dispose并清除CompositeDisposable、c# – IDisposable问题、c# – 为什么我的IDisposable对象上的Dispose()方法不可用?等相关知识的信息别忘了在本站进行查找喔。

本文标签: