随笔 - 22  文章 - 0 评论 - 63 trackbacks - 1
<2007年12月>
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345

与我联系

常用链接

留言簿(1)

我的标签

随笔分类

随笔档案

搜索

  •  

最新评论

阅读排行榜

评论排行榜

前言: 之前在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 on 2007-12-28 16:08 在天空飞翔 阅读(2186) 评论(12)  编辑 收藏 所属分类: C#

FeedBack:
#1楼  2007-12-28 16:25 装配脑袋      
问题的关键在于,有人不知道什么叫“非托管资源的清理”,以至于还是不能正确实现Dispose
  回复  引用  查看    
#2楼  2007-12-28 17:38 Terry Sun      
实用的文章
偶在上一个项目中有用到了继承IDisposable的方法,Dispose(bool disposing)
这个方法比偶写的巧,3Q
  回复  引用  查看    
#3楼  2007-12-28 18:56       
说了还是没说。

到底怎么从内存清理。

GC.SuppressFinalize(this);
这句话根本没用
  回复  引用  查看    
#4楼  2007-12-28 20:32 望穿秋水      
//该函数为手动调用,此处可进行托管资源的清理
这里具体该怎样写对象的释放?很不解?
  回复  引用  查看    
#5楼  2007-12-28 23:28 Cat Chen      
我觉得这个写法是线程不安全的。
  回复  引用  查看    
#6楼  2007-12-29 01:44 Henry Liang      
@装配脑袋
你说的有道理。
  回复  引用  查看    
#7楼  2007-12-29 02:48 corvallis [未注册用户]
My 2 cents:

In Dispose method, maybe move GC.SuppressFinalize(this) above the Dispose(True) call. It is a little better to inform GC ealier.

Simliarly, move disposed=true just after the if condition. This can help reducing the thread-safety issue since release unmanaged resource can take a while. (not 100% although)


  回复  引用    
#8楼  2007-12-29 09:12 Anytao      
常见的非托管资源包括:文件句柄、数据库链接、位图、网络链接、COM对象、互斥体等,使用Dispose就得了解哪些资源在这里释放。

推荐同时实现Finalize和Dipose,以确保非托管资源一定被释放。
  回复  引用  查看    
@辰

GC.SuppressFinalize(this); 这句话怎么会没用呢?

加了这句后, Finalize 方法就不会执行了,文章中不是说了吗?
  回复  引用    
--引用--------------------------------------------------
装配脑袋: 问题的关键在于,有人不知道什么叫“非托管资源的清理”,以至于还是不能正确实现Dispose
--------------------------------------------------------
#8楼 2007-12-29 09:12 Anytao

已经回答了, 不同的非托管资源, 清理方法不一样啊, 比如打开的文件句柄要用 CloseHandle 关掉, GDI 的DC 要用 ReleaseDC 关掉,等
  回复  引用    
#11楼  2007-12-29 12:16 芭蕉      
.Net components上有一个实现我觉得还不错,感兴趣的可以去看看,Lifecycle Management那一章的最后一个例子就是了。
  回复  引用  查看    
#12楼  2007-12-29 14:00 BlueMountain      
Jeffrey Richter. net框架程序设计技术内幕, 李建忠翻译, 说得非常清楚了。
  回复  引用  查看    

标题  
姓名  
主页
Email (只有博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2007-12-29 10:16 编辑过
 
另存  打印