通过阅读Microsoft文档,我知道,IDisposable接口的“主要”用途是清理非托管资源
对我来说,“非托管”意味着数据库连接、套接字、窗口句柄等。但是,我见过一些代码,其中实现了Dispose()方法来释放托管的资源,这对我来说似乎是多余的,因为垃圾收集器应该为您处理这些资源
例如:
公共类MyCollection:IDisposable
{
私有列表<;字符串>;_theList=新列表<;字符串>;();
私有字典<;字符串,点>;_theDict=新字典<;字符串,点>;();
//死吧,清理它!(释放非托管资源)
公共空间处置()
{
_清除列表();
_theDict.clear();
_列表=空;
_theDict=null;
}
我的问题是,这是否会使垃圾收集器释放MyCollection使用的内存的速度比正常情况下快
edit:到目前为止,人们已经发布了一些使用IDisposable清理非托管资源(如数据库连接和位图)的好例子。但是,假设上面代码中的\u列表包含一百万个字符串,并且您希望立即释放该内存,而不是等待垃圾收集器。上面的代码可以实现这一点吗
Dispose的作用是释放非托管资源。它需要在某个时候完成,否则它们将永远不会被清理。垃圾收集器不知道如何调用DeleteHandle()对于类型为IntPtr的变量,它不知道是否需要调用DeleteHandle()
注意:什么是非托管资源?如果您在Microsoft.NET Framework中找到它:它是托管的。如果您自己去查看MSDN,它是非托管的。任何您使用p/Invoke调用从.NET Framework中提供给您的所有东西的舒适世界中走出的东西都是非托管的,您现在可以恢复了负责清理
您创建的对象需要公开一些外部世界可以调用的方法,以便清理非托管资源。该方法可以命名为任何您喜欢的名称:
public void Cleanup()
或
公共无效关闭()
但是,这种方法有一个标准化的名称:
public void Dispose()
甚至创建了一个接口,IDisposable,它只有一个方法:
公共接口IDisposable
{
无效处置()
}
因此,您可以让对象公开IDisposable接口,这样您就可以保证已经编写了一个方法来清理非托管资源:
public void Dispose()
{
Win32.DestroyHandle(此.CursorFileBitMapiceHandle);
}
你完成了。除非你能做得更好。
如果您的对象分配了250MB的System.Drawing.Bitmap(即.NET托管位图类),该怎么办作为某种帧缓冲区?当然,这是一个托管的.NET对象,垃圾收集器将释放它。但您真的想让250MB的内存留在那里吗?等待垃圾收集器最终出现并释放它?如果有一个开放的数据库连接怎么办?我们当然不想让该连接存在打开,等待GC完成对象
如果用户调用了Dispose()(这意味着他们不再计划使用该对象),为什么不去掉那些浪费的位图和数据库连接呢
因此,现在我们将:
- 摆脱非托管资源(因为我们必须这样做),并且
- 摆脱托管资源(因为我们想提供帮助)
因此,让我们更新我们的Dispose()方法以摆脱这些托管对象:
public void Dispose()
{
//自由非托管资源
Win32.DestroyHandle(此.CursorFileBitMapiceHandle);
//还有免费的托管资源
if(this.databaseConnection!=null)
{
这个.databaseConnection.Dispose();
this.databaseConnection=null;
}
if(this.frameBufferImage!=null)
{
这个.frameBufferImage.Dispose();
this.frameBufferImage=null;
}
}
一切都很好,除了你能做得更好
如果此人忘记调用对象上的Dispose(),会怎么样?那么他们会泄漏一些未管理的资源
注意:它们不会泄漏托管的资源,因为垃圾收集器最终将在后台线程上运行,并释放与任何未使用对象相关联的内存。这将包括您的对象和您使用的任何托管对象(例如
位图和数据库连接)
如果这个人忘记调用Dispose(),我们仍然可以保存他们的培根!我们仍然可以为他们调用:当垃圾收集器最终释放(即完成)我们的对象时
注意:垃圾收集器最终将释放所有托管对象。
当它这样做时,它调用Finalize
方法。GC不知道,或者
关心你的处置方法。
那只是我们选择的名字
当我们想要获取时调用的方法
摆脱不受管理的东西
垃圾收集器对对象的破坏是释放那些讨厌的非托管资源的最佳时机。我们通过重写Finalize()方法来实现这一点
注意:在C#中,没有显式重写
Finalize()方法。
您编写了一个类似于C++析构函数的方法,并且
编译器将其作为Finalize()方法的实现:
~MyObject()
{
//我们正在完成(即销毁),请调用Dispose以防用户忘记处理
Dispose();//<;--警告:微妙的错误!继续阅读!
}
但是这段代码中有一个bug。你看,垃圾收集器在后台线程上运行;你不知道两个对象被销毁的顺序。完全有可能在你的Dispose()代码中,你试图删除的托管的对象(因为你想提供帮助)不再存在:
public void Dispose()
{
//自由非托管资源
Win32.DestroyHandle(此.gdiCursorBitmapStreamFileHandle);
//还有免费的托管资源
if(this.databaseConnection!=null)
{
此.databaseConnection.Dispose();/<;--崩溃,GC已将其销毁
this.databaseConnection=null;
}
if(this.frameBufferImage!=null)
{
此.frameBufferImage.Dispose();/<;--崩溃,GC已将其销毁
this.frameBufferImage=null;
}
}
因此,您需要的是一种方法,让Finalize()告诉Dispose(),它应该不接触任何托管的资源(因为它们可能不再存在了),同时仍然释放非托管的资源
执行此操作的标准模式是让Finalize()和Dispose()都调用第三个(!)方法;如果从Dispose()调用它,则传递一个布尔语句(与Finalize()相反),这意味着释放托管资源是安全的
这种内部方法可以被赋予一些任意名称,如“CoreDispose”或“MyInternalDispose”,但传统上称之为Dispose(布尔):
受保护的无效处置(布尔处置)
但更有用的参数名称可能是:
受保护的void Dispose(布尔值ITissafetoalsoferemanagedObjects)
{
//自由非托管资源
Win32.DestroyHandle(此.CursorFileBitMapiceHandle);
//也可以释放托管资源,但前提是从Dispose调用我
//(如果从Finalize调用我,则对象可能不存在
//再
if(ITISSAFETOALSOFEREMANAGEDOBJECTS)
{
if(this.databaseConnection!=null)
{
这个.databaseConnection.Dispose();
this.databaseConnection=null;
}
if(this.frameBufferImage!=null)
{
这个.frameBufferImage.Dispose();
this.frameBufferImage=null;
}
}
}
然后将IDisposable.Dispose()方法的实现更改为:
public void Dispose()
{
Dispose(对);//我在Dispose给你打电话,很安全
}
以及您的终结器:
~MyObject()
{
Dispose(false);//我不是从Dispose给你打电话,这不安全
}
注意:如果您的对象是从实现了
Dispose的对象派生的,那么在重写Dispose时不要忘记调用它们的baseDispose方法:
public override void Dispose()
{
尝试
{
Dispose(true);//true:安全释放托管资源
}
最后
{
base.Dispose();
}
}
一切都很好,除了你能做得更好
如果用户对您的对象调用Dispose(),则所有内容都已清理。稍后,当垃圾收集器出现并调用Finalize时,它将再次调用Dispose
这不仅是浪费,而且如果您的对象包含从上次调用Dispose()到已经处理的对象的垃圾引用,您将尝试dispo