木子屋 Dnawo's BLOG

IDisposable接口和析构函数

👤 dnawo 📅 2007-12-16 👁 5595 👍 0 💬 0 🔄 老翅寒暑
其实这是一个老话题了,只是刚才做程序的时候突然发现自己有些概念有点模糊,所以做了一个测试如下:

这段代码是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 
 
*/ 
}

从运行结果我们可以分析出如下的结果:

1.如果调用Dispose进行释放,则系统不会调用其析构函数
2.如果是系统自动释放的,则不会调用其Dispose函数,也就是说,.net垃圾收集器其实不认识 IDisposable接口。
3.在类析构过程中,会先释放其中的成员,最后再调用类的析构函数本身。

这里第3点需要说明一下,这是和C++中区别很大的地方,在C++中所有的释放都需要类的析构函数来进行,也就是在C++的析构函数中,所有的成员都是可用而且有效的。但是在C#中就会有区别了,因为C#的类析构函数调用之前,系统就会进行托管成员对象的清理工作。从生命周期的观点来看,类成员的生命周期必须包含在类的生命周期之内,C#这么做没有问题,但是问题时在于程序经常需要在析构函数中使用类的成员,所以就出现了异常情况:在析构函数中使用的类成员已经被析构了!也就是说,同样在析构函数范围内,C++包含的寓意是“析构=无效”,但是在C#的析构函数范围中,“析构 != 无效”。

由此,我们明白了为什么一些代码检查的软件会建议我们实现IDisposable时候,像例子中那样要自定义一个Dispose(bool disposing)了。

评论(0)

暂无评论。

计算题
评论需审核通过后显示
← 上一篇 构造函数和析构函数的原理及在C#中的运用 下一篇 → C#接口范例:USB接口的实现