代码改变世界

C#中标准Dispose模式的实现与使用(条目17 实现标准的销毁模式)

2013-11-28 11:04  咒语  阅读(1156)  评论(0编辑  收藏  举报

实现了Dispose模式与实现了IDisposable接口的区别就是:IDisposable的实现的可靠性(释放相关资源)要靠编程人员来解决(你确信你从来都一直调用了Dispose(Close)方法吗?),而实现了Dispose模式后,当编程人员没有主动调用Dispose方法时,会由终结器来调用(有些时候编程人员想主动调用也调用不了,比如远程连上来的TcpChannel,客户端断开时,服务端只能由终者器调用)。

Dispose模式()的实现需要以下4个步骤:
1. 释放所有非托管资源;
2. 释放所有托管资源,包括释放事件监听程序;
3. 设计一个状态标志(IsDisposed),表示该对像已经被销毁。若是在销毁后再次调用对像的公有方法,那么应该抛出ObjectDisposed异常;
4. 跳过终者操作,调用CG.SuppressFinalize(this)即可。

注:
------------------
托管资源:由CLR管理分配和释放的资源,即由CLR里new出来的对象;
非托管资源:不受CLR管理的对象,windows内核对象,如文件、数据库连接、套接字、COM对象等;

用实现来说明。
EXAMPLE CODE:

using System;

namespace CS.DesignPatterns
{
    /// <summary>
    /// 示例
    /// </summary>
    public class DisposePatternSample:IDisposable
    {
        private bool _isDisposed;//是否已释放了资源,true时方法都不可用了。

        public DisposePatternSample()
        {
            _isDisposed = false;
        }

        public void MethodSample()
        {
            if(_isDisposed)
                throw new ObjectDisposedException("inner resource is disposed.");
            Console.WriteLine("MethodSample is called.");
        }

        /// <summary>
        /// 为继承类释放时使用
        /// <remarks>
        /// Note:这儿为什么要写成虚方法呢?
        /// 1. 为了让派生类清理自已的资源。将销毁和析构的共同工作提取出来,并让派生类也可以释放其自已分配的资源。
        /// 2. 为派生类提供了根据Dispose()或终结器的需要进行资源清理的必要入口。
        /// </remarks>
        /// </summary>
        /// <param name="isDisposing"></param>
        protected virtual void Dispose(bool isDisposing)
        {
            if(_isDisposed)return;
            
            if (isDisposing)
            {
                //释放托管资源
                //(由CLR管理分配和释放的资源,即由CLR里new出来的对象)
                //TODO: other code
            }

            //释放非托管资源
            //(不受CLR管理的对象,windows内核对象,如文件、数据库连接、套接字、COM对象等)
            //TODO: other code

            _isDisposed = true;
        }

        #region IDisposable 成员

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion

        ///// <summary>
        ///// 如果没有非托管资源,不要实现它
        ///// </summary>
        //~DisposePatternSample()
        //{
        //    Dispose(false);
        //}
    }


    public class DrivedDisposePatternSample : DisposePatternSample
    {
        private bool _isDisposed = false; //各类维护自已的释放状态,把可能出现的错误限制在类本身

        /// <summary>
        /// 子类清理自已的资源时使用
        /// </summary>
        /// <param name="isDisposing"></param>
        protected override void Dispose(bool isDisposing)
        {
            if(_isDisposed)return;
            if (isDisposing)
            {
                //释放托管资源
                //(由CLR管理分配和释放的资源,即由CLR里new出来的对象)
                //TODO: other code
            }

            //释放非托管资源
            //(不受CLR管理的对象,windows内核对象,如文件、数据库连接、套接字、COM对象等)
            //TODO: other code

            //基类释放资源
            //基类负责调用GC.SuppressFinalize()
            base.Dispose(isDisposing);

            _isDisposed = true;
        }

        ///// <summary>
        ///// 如果没有非托管资源,不要实现它
        ///// </summary>
        //~DrivedDisposePatternSample()
        //{
        //    Dispose(false);
        //}

    }

}

 

上述的代码并没有使用析构方法,这是因为上述的代码并没有使有非托管资源(永远不会调用Dispose(false))方法。注意:除非你调用了非托管资源,否则不要实现析构方法。即使析构器永远不会被调用,它本身也会极大的影响类型的性能。不要画蛇添足。不过这个模式却不能改变,因为你的派生类可能会用到非托管的资源。

重要的建议:只能在Dispose方法中释放资源,不得进行其它操作(如果一不小心的其它操作,让本该死亡的对象起死回生,哼哼~~~~)。

请遵守Dispose标准模式的实现,这会节省你,你的类型使用者,你的类型的派生者的大量的时间。