C# 正确使用IDisposable接口
我从阅读中了解到,C# 正确使用IDisposable接口,c#,.net,garbage-collection,idisposable,C#,.net,Garbage Collection,Idisposable,我从阅读中了解到,IDisposable接口的“主要”用途是清理非托管资源 对我来说,“非托管”意味着数据库连接、套接字、窗口句柄等。但是,我见过一些代码,其中实现了Dispose()方法来释放托管资源,这对我来说似乎是多余的,因为垃圾回收器应该为您处理这些 例如: public class MyCollection : IDisposable { private List<String> _theList = new List<String>(); pr
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=新列表();
专用词典_theDict=新词典();
//死吧,清理它!(释放非托管资源)
公共空间处置()
{
_清除列表();
_theDict.clear();
_列表=空;
_theDict=null;
}
我的问题是,这是否会使垃圾收集器释放MyCollection使用的内存的速度比正常情况下快
编辑:到目前为止,人们已经发布了一些使用IDisposable清理非托管资源(如数据库连接和位图)的好例子。但是假设上面代码中的
\u列表
包含一百万个字符串,并且您希望现在就释放内存,而不是等待垃圾收集器。您会这样做吗上面的代码实现了这一点?是的,这段代码是完全冗余和不必要的,它不会让垃圾收集器做任何本来不会做的事情(即,一旦MyCollection的实例超出范围),尤其是.Clear()
调用
对编辑的回答:有点。如果我这样做:
public void WasteMemory()
{
var instance = new MyCollection(); // this one has no Dispose() method
instance.FillItWithAMillionStrings();
}
// 1 million strings are in memory, but marked for reclamation by the GC
出于内存管理的目的,其功能与此相同:
public void WasteMemory()
{
var instance = new MyCollection(); // this one has your Dispose()
instance.FillItWithAMillionStrings();
instance.Dispose();
}
// 1 million strings are in memory, but marked for reclamation by the GC
如果您真的需要立即释放内存,请调用
GC.Collect()
。不过,这里没有理由这样做。内存将在需要时释放。IDisposable
通常用于利用using
语句,并利用一种简单的方法对托管对象进行确定性清理
public class LoggingContext : IDisposable {
public Finicky(string name) {
Log.Write("Entering Log Context {0}", name);
Log.Indent();
}
public void Dispose() {
Log.Outdent();
}
public static void Main() {
Log.Write("Some initial stuff.");
try {
using(new LoggingContext()) {
Log.Write("Some stuff inside the context.");
throw new Exception();
}
} catch {
Log.Write("Man, that was a heavy exception caught from inside a child logging context!");
} finally {
Log.Write("Some final stuff.");
}
}
}
如果
MyCollection
无论如何都要进行垃圾收集,那么您不需要对其进行处理。这样做只会对CPU造成不必要的干扰,甚至可能使垃圾收集器已经执行的某些预先计算的分析无效
我使用IDisposable
来确保线程与非托管资源一起被正确处置
编辑回应斯科特的评论:
GC性能指标仅在调用[sic]GC.Collect()时受到影响”
从概念上讲,GC维护对象引用图的视图,以及从线程堆栈帧对其进行的所有引用。此堆可能相当大,并跨越许多内存页。作为一种优化,GC缓存其对不太可能经常更改的页的分析,以避免不必要地重新扫描页。GC收到当页面中的数据发生更改时,内核会发出通知,因此它知道页面脏了,需要重新扫描。如果集合在Gen0中,则页面中的其他内容可能也在更改,但在Gen1和Gen2中这种情况不太可能发生。有趣的是,对于将GC移植到Mac i的团队来说,Mac OS X中没有这些挂钩n以使Silverlight插件在该平台上工作
反对不必要的资源处置的另一点:设想一个进程正在卸载的情况。还设想该进程已经运行了一段时间。该进程的许多内存页可能已经交换到磁盘。至少它们不再位于一级或二级缓存中。在这种情况下,没有f点或正在卸载以将所有数据和代码页交换回内存的应用程序,以便在进程终止时“释放”操作系统将释放的资源。这适用于托管资源,甚至某些非托管资源。否则,必须仅释放使非后台线程保持活动状态的资源这一进程将继续下去
现在,在正常执行期间,必须正确清理临时资源(正如@fezmonkey指出的数据库连接、套接字、窗口句柄),以避免非托管内存泄漏。这些都是必须处理的事情。如果您创建了某个拥有线程的类(我所说的owns是指它创建了它,因此负责确保它停止,至少按照我的编码风格),那么该类很可能必须实现IDisposable
,并在Dispose
期间中断线程
NET framework使用
IDisposable
接口作为信号,甚至警告开发人员必须处理此类。我想不出框架中有任何类型实现IDisposable
(不包括显式接口实现)其中Dispose是可选的。Dispose模式的目的是提供一种机制来清理托管和非托管资源,何时清理取决于Dispose方法的调用方式。在您的示例中,Dispose的使用实际上并没有做任何与Dispose相关的事情,因为清除列表对该方法没有影响同样,将变量设置为null的调用也不会对GC产生影响
有关如何实现Dispose模式的更多详细信息,您可以查看以下内容,但基本上如下所示:
public class SimpleCleanup : IDisposable
{
// some fields that require cleanup
private SafeHandle handle;
private bool disposed = false; // to detect redundant calls
public SimpleCleanup()
{
this.handle = /*...*/;
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// Dispose managed resources.
if (handle != null)
{
handle.Dispose();
}
}
// Dispose unmanaged managed resources.
disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
这里最重要的方法是Dispose(bool),它实际上在两种不同的情况下运行:
- disposing==true:该方法已被用户的代码直接或间接调用。托管和非托管资源都可以被释放
- disposing==false:该方法已由运行时从终结器内部调用,您不应引用其他对象。仅
public void Cleanup()
public void Shutdown()
public void Dispose()
public interface IDisposable { void Dispose() }
public void Dispose() { Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle); }
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; } }
~MyObject() { //we're being finalized (i.e. destroyed), call Dispose in case the user forgot to Dispose(); //<--Warning: subtle bug! Keep reading! }
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; } }
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; } } }
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 }
public override void Dispose() { try { Dispose(true); //true: safe to free managed resources } finally { base.Dispose(); } }
protected void Dispose(Boolean iAmBeingCalledFromDisposeAndNotFinalize) { //Free unmanaged resources Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle); //<--double destroy ... }
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 }
~MyObject() { //Free unmanaged resources Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle); //A C# destructor automatically calls the destructor of its base class. }
class MyClass : IDisposable { // ... #region IDisposable Members and Helpers private bool disposed = false; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (!this.disposed) { if (disposing) { // cleanup code goes here } disposed = true; } } ~MyClass() { Dispose(false); } #endregion }
public static void Indented(this Log log, Action action) { log.Indent(); try { action(); } finally { log.Outdent(); } }
Log.Write("Message at the top"); Log.Indented(() => { Log.Write("And this is indented"); Log.Indented(() => { Log.Write("This is even more indented"); }); }); Log.Write("Back at the outermost level again");
using( MiniTx tx = new MiniTx() ) { // code that might not work. tx.Commit(); }
Public Class LargeStuff Implements IDisposable Private _Large as string() 'Some strange code that means _Large now contains several million long strings. Public Sub Dispose() Implements IDisposable.Dispose _Large=Nothing End Subusing( LogTimer log = new LogTimer("MyCategory", "Some message") ) { // code to time... }
public class SomeClass : IDisposable { /// <summary> /// As usually I don't care was object disposed or not /// </summary> public void SomeMethod() { if (_disposed) throw new ObjectDisposedException("SomeClass instance been disposed"); } public void Dispose() { Dispose(true); } private bool _disposed; protected virtual void Dispose(bool disposing) { if (_disposed) return; if (disposing)//we are in the first call { } _disposed = true; } }
public class DisposeExample { // A base class that implements IDisposable. // By implementing IDisposable, you are announcing that // instances of this type allocate scarce resources. public class MyResource: IDisposable { // Pointer to an external unmanaged resource. private IntPtr handle; // Other managed resource this class uses. private Component component = new Component(); // Track whether Dispose has been called. private bool disposed = false; // The class constructor. public MyResource(IntPtr handle) { this.handle = handle; } // Implement IDisposable. // Do not make this method virtual. // A derived class should not be able to override this method. public void Dispose() { Dispose(true); // This object will be cleaned up by the Dispose method. // Therefore, you should call GC.SupressFinalize to // take this object off the finalization queue // and prevent finalization code for this object // from executing a second time. GC.SuppressFinalize(this); } // Dispose(bool disposing) executes in two distinct scenarios. // If disposing equals true, the method has been called directly // or indirectly by a user's code. Managed and unmanaged resources // can be disposed. // If disposing equals false, the method has been called by the // runtime from inside the finalizer and you should not reference // other objects. Only unmanaged resources can be disposed. protected virtual void Dispose(bool disposing) { // Check to see if Dispose has already been called. if(!this.disposed) { // If disposing equals true, dispose all managed // and unmanaged resources. if(disposing) { // Dispose managed resources. component.Dispose(); } // Call the appropriate methods to clean up // unmanaged resources here. // If disposing is false, // only the following code is executed. CloseHandle(handle); handle = IntPtr.Zero; // Note disposing has been done. disposed = true; } } // Use interop to call the method necessary // to clean up the unmanaged resource. [System.Runtime.InteropServices.DllImport("Kernel32")] private extern static Boolean CloseHandle(IntPtr handle); // Use C# destructor syntax for finalization code. // This destructor will run only if the Dispose method // does not get called. // It gives your base class the opportunity to finalize. // Do not provide destructors in types derived from this class. ~MyResource() { // Do not re-create Dispose clean-up code here. // Calling Dispose(false) is optimal in terms of // readability and maintainability. Dispose(false); } } public static void Main() { // Insert code here to create // and use the MyResource object. } }