IDisposable接口和析构函数
其实这是一个老话题了,只是刚才做程序的时候突然发现自己有些概念有点模糊,所以做了一个测试如下:
这段代码是MSDN中的例子修改而来。
using System;
using System.ComponentModel;
// The following example demonstrates how to create
// a resource class that implements the IDisposable interface
// and the IDisposable.Dispose method.
public class DisposeExample
{
public class MyDisposableMember: IDisposable
{
private bool disposed = false;
private string info;
public MyDisposableMember(string _info)
{
info = _info;
System.Diagnostics.Debug.WriteLine("\tMyDisposableMember:" + info);
}
public void Dispose()
{
System.Diagnostics.Debug.WriteLine("\tDispose:" + info);
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if(!this.disposed)
{
if(disposing)
{
}
}
disposed = true;
}
~MyDisposableMember()
{
System.Diagnostics.Debug.WriteLine("\t~MyDisposableMember():" + info);
Dispose(false);
}
}
// A base class that implements IDisposable.
// By implementing IDisposable, you are announcing that
// instances of this type allocate scarce resources.
public class MyResource: IDisposable
{
// Track whether Dispose has been called.
private bool disposed = false;
private string info;
MyDisposableMember member;
// The class constructor.
public MyResource(string _info)
{
info = _info;
System.Diagnostics.Debug.WriteLine("MyResource:" + info);
member = new MyDisposableMember("member-" + _info);
}
// Implement IDisposable.
// Do not make this method virtual.
// A derived class should not be able to override this method.
public void Dispose()
{
System.Diagnostics.Debug.WriteLine("Dispose:" + info);
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.
private void Dispose(bool disposing)
{
if(!this.disposed)
{
// If disposing equals true, dispose all managed
// and unmanaged resources.
if(disposing)
{
// Dispose managed resources.
member.Dispose();
}
// Call the appropriate methods to clean up
// unmanaged resources here.
// If disposing is false,
// only the following code is executed.
member = null;
}
disposed = true;
}
// 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.
System.Diagnostics.Debug.WriteLine("~MyResource():" + info);
Dispose(false);
}
}
public static void Main()
{
// Insert code here to create
// and use the MyResource object.
MyResource mr = new MyResource("mr");
mr.Dispose();
new MyResource("hang");
using(MyResource m = new MyResource("using"))
System.Diagnostics.Debug.WriteLine("ready to exit using statement");
System.GC.SuppressFinalize(mr);
}
/*
运行结果如下:
MyResource:mr
MyDisposableMember:member-mr
Dispose:mr
Dispose:member-mr
MyResource:hang
MyDisposableMember:member-hang
MyResource:using
MyDisposableMember:member-using
ready to exit using statement
Dispose:using
Dispose:member-using
~MyDisposableMember():member-hang
~MyResource():hang
*/
}
using System.ComponentModel;
// The following example demonstrates how to create
// a resource class that implements the IDisposable interface
// and the IDisposable.Dispose method.
public class DisposeExample
{
public class MyDisposableMember: IDisposable
{
private bool disposed = false;
private string info;
public MyDisposableMember(string _info)
{
info = _info;
System.Diagnostics.Debug.WriteLine("\tMyDisposableMember:" + info);
}
public void Dispose()
{
System.Diagnostics.Debug.WriteLine("\tDispose:" + info);
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if(!this.disposed)
{
if(disposing)
{
}
}
disposed = true;
}
~MyDisposableMember()
{
System.Diagnostics.Debug.WriteLine("\t~MyDisposableMember():" + info);
Dispose(false);
}
}
// A base class that implements IDisposable.
// By implementing IDisposable, you are announcing that
// instances of this type allocate scarce resources.
public class MyResource: IDisposable
{
// Track whether Dispose has been called.
private bool disposed = false;
private string info;
MyDisposableMember member;
// The class constructor.
public MyResource(string _info)
{
info = _info;
System.Diagnostics.Debug.WriteLine("MyResource:" + info);
member = new MyDisposableMember("member-" + _info);
}
// Implement IDisposable.
// Do not make this method virtual.
// A derived class should not be able to override this method.
public void Dispose()
{
System.Diagnostics.Debug.WriteLine("Dispose:" + info);
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.
private void Dispose(bool disposing)
{
if(!this.disposed)
{
// If disposing equals true, dispose all managed
// and unmanaged resources.
if(disposing)
{
// Dispose managed resources.
member.Dispose();
}
// Call the appropriate methods to clean up
// unmanaged resources here.
// If disposing is false,
// only the following code is executed.
member = null;
}
disposed = true;
}
// 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.
System.Diagnostics.Debug.WriteLine("~MyResource():" + info);
Dispose(false);
}
}
public static void Main()
{
// Insert code here to create
// and use the MyResource object.
MyResource mr = new MyResource("mr");
mr.Dispose();
new MyResource("hang");
using(MyResource m = new MyResource("using"))
System.Diagnostics.Debug.WriteLine("ready to exit using statement");
System.GC.SuppressFinalize(mr);
}
/*
运行结果如下:
MyResource:mr
MyDisposableMember:member-mr
Dispose:mr
Dispose:member-mr
MyResource:hang
MyDisposableMember:member-hang
MyResource:using
MyDisposableMember:member-using
ready to exit using statement
Dispose:using
Dispose:member-using
~MyDisposableMember():member-hang
~MyResource():hang
*/
}
从运行结果我们可以分析出如下的结果:
1。如果调用Dispose进行释放,则系统不会调用其析构函数
2。如果是系统自动释放的,则不会调用其Dispose函数,也就是说,.net垃圾收集器其实不认识 IDisposable接口。
3。在类析构过程中,会先释放其中的成员,最后再调用类的析构函数本身。
这里第3点需要说明一下,这是和C++中区别很大的地方,在C++中所有的释放都需要类的析构函数来进行,也就是在C++的析构函数中,所有的成员都是可用而且有效的。但是在C#中就会有区别了,因为C#的类析构函数调用之前,系统就会进行托管成员对象的清理工作。从生命周期的观点来看,类成员的生命周期必须包含在类的生命周期之内,C#这么做没有问题,但是问题时在于程序经常需要在析构函数中使用类的成员,所以就出现了异常情况:在析构函数中使用的类成员已经被析构了!也就是说,同样在析构函数范围内,C++包含的寓意是“析构=无效”,但是在C#的析构函数范围中,“析构 != 无效”。
由此,我们明白了为什么一些代码检查的软件会建议我们实现IDisposable时候,象例子中那样要自定义一个Dispose(bool disposing)了。