正确实现 IDisposable 接口

前言: 之前在M$的一本工具书上看过该节内容,但慢慢地就忘记了, 在实际项目遇到类似的问题又要 google, 汗下, 于是写此篇blog作备忘录 。


虽然 .net 有垃圾回收机制(GC), 可自动进行大部分的资源清理工作,但开发人员仍旧应该养成良好的通过手动调用资源清理方法来进行资源回收的习惯, 让对象实现 IDisposable 接口正是 M$ 所推荐的, 该接口的原型为:
    // Summary:
    
//     Defines a method to release allocated unmanaged resources.
    [ComVisible(true)]
    
public interface IDisposable
    
{
        
// Summary:
        
//     Performs application-defined tasks associated with freeing, releasing, or
        
//     resetting unmanaged resources.
        void Dispose();
    }

 注:.net 内部也有传说中的”析构函数“,就是 Finalize(), 该函数由GC自动调用。

实现 Dispose 需要注意的是
1. 如果资源已手动释放过了,就应该通知GC,不要再对该对象执行 Finalize 操作
2. 确保重复执行 Dispose 时不会出错

根据以上规则, 简单的定义一个类 classSample
    public class classSample : IDisposable
    
{
        
/// <summary>
        
/// 该对象是否已被销毁过
        
/// </summary>

        private bool disposed;

        
/// <summary>
        
/// 这就是传说中的 Finalize函数,是不是和C++的析构函数很像?
        
/// </summary>

        ~classSample()
        
{
            Console.WriteLine(
"对象被销毁");
            
            Dispose(
false);
        }


        
public void Dispose()
        
{
            Console.WriteLine(
"手动销毁对象");
            Dispose(
true);
            
//通知GC, 偶已经被销毁过了,可以不用再管偶了。。。
            GC.SuppressFinalize(this);
        }


        
/// <summary>
        
/// Dispose
        
/// </summary>
        
/// <param name="disposing">是否由程序直接调用</param>

        protected void Dispose(bool disposing)
        
{
            
if (!disposed)
            
{
                
if (disposing)
                
{
                    
//该函数为手动调用,此处可进行托管资源的清理
                    
//比如此类中有一个类型为 DataSet 的变量 dsData
                    
//此处可调用该对象的 Dispose 方法来清理托管资源
                    dsData.Dispose();
                }


                
//进行非托管资源的清理
                
//非托管的资源主要为一些用 API 打开的文件句柄,设备场景句柄等
                
//该类资源 GC 是无法管理的,只能依靠程序员自已释放
                
//不同的资源, 释放方法不一样
                //例如:
                
//释放文件句柄 (定义API的代码略。)
                CloseHandle(handle);
                
//释放GDI 中的设备场景 (定义API的代码略。)
                ReleaseDC(hdc);
            }

            disposed 
= true;
        }

    }

注意protected void Dispose(bool disposing) 函数, 清理托管资源和非托管资源的代码位置不要写反了,否则当一个托管对象已经被GC回收后,再去调用它的Dispose 方法, 系统会触发一个 ObjectDisposedException 异常

到这里 ,一个简单的实现
IDisposable 的类已经写好了, 再写段测试代码看看
        private void button1_Click(object sender, EventArgs e)
        
{
            classSample cls 
= new classSample();
        }
点击按钮, 再关闭程序, 可看到 output 窗口中显示 对象被销毁 说明  Finalize 函数被执行了

换一个
        private void button1_Click(object sender, EventArgs e)
        
{
            classSample cls 
= new classSample();
            cls.Dispose();
        }
再看 output 窗口中,只显示了”手动销毁对象“, 说明在手动执行Dispose 后,已正确通知GC,不再执行Finalize函数。

小技巧: 可使用 using 关键字来定义对象的生存期,
超出 using范围 后,如果对象实现了
IDisposable接口, 系统会自动调用其 Dispose 方法, 是不是很方便。
            using (classSample cls = new classSample())
            
{
            }

看,我并没有调用 Dispose 方法, output 窗口中还是会显示 ”手动销毁对象“.
posted @ 2007-12-28 16:08  在天空飞翔  阅读(3395)  评论(13编辑  收藏  举报