safehandle 和析构函数

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了safehandle 和析构函数相关的知识,希望对你有一定的参考价值。

safehandle 是一种析构机制,她和析构函数有什么分别。

 

首先要理解析构函数。析构函数在.net中是没有顺序的,因此你不能假定另一个对象的析构函数在你之后运行,哪怕它是你的成员!如果你的成员也有析构函数,那么你能做什么,什么不应该做?

第一,你不应该假设它没清理,而去试图清理它。合理的做法是它应该自己实现dispose模式,你在dispose(false)段落可以调用它的dispose()函数.因为dispose是可多次重入的,因此不会有问题。当然你也可以不理他。

第二,一个实现了dispose的.net对象,外部应该怎样看它?不应该把它所控制的资源看作非托管资源,既然他实现了dispose,也就是“有自行管理”的,因此应该在dispose(false)段落调用。这是我对dispose模式的理解。dispose模式分两类资源,一类是托管,一类是非托管。托管和非托管并不是纯粹站在.net角度出发,而是应该理解为“自己管理”和“其他对象管理”的差别。你的对象自己打开文件,就自己负责关闭,而其他对象,包括成员对象,他们控制的资源就由他们控制,在当前立场上看,都是“受托管的”。

这样的好处是,我可以分别控制每一个实现dispose模式的对象成员的控制时机,而dispose(false)也就是析构函数,能够作为防御性代码,帮我把忘记的对象资源清理掉。当然也有坏处,如果你希望dispose就是关闭所有资源,它就没有这个效果。但是dispose有两种实现模式,一种是没有析构函数做组合的,可以采用这种统一模式,也不需要依赖防御性代码了,当然这个清理任务就交给你了。

然后是safehandle,safehandle据说有经过优化,但是它也不会抢先在析构函数阶段运行,在我测试中是这种情况。因此,我不知道优化在那里了。只是添加了一种模式,比析构函数更舵控制,毕竟是外部独立的类,而且系统已经针对多种资源提供了恰当的子类,可惜没有针对com对象资源。

 

总的来说,最佳实践是:

1.如果safehandle子类有的系统资源,如句柄,非托管内存等等,用safehandle 模式。

2.如果子成员正确实现了dispose,用带析构函数的dispose模式。

3.如果子成员没有实现dispose,但是控制了相关资源,也就是你要负责子对象相关资源的显式控制,千万别用析构函数清理,因为析构函数阶段,子成员可能已经把它关联的资源给释放掉了(未必以正确的方式,只是对资源失去控制力),你无法再清理。因为GC对任意对象的析构函数调用顺序是不确定的。(这一状况只能要求你在编程阶段正确调用dispose,而绝不能遗忘调用,否则就有资源泄漏。com for .net就是如此设计的)

4.其他情况,可以用析构函数做防御性编程。

5.dispose的实现方式要规范。

 

规范的dispose该注意哪些,这里补充一下:

        private bool disposedValue = false; // 要检测冗余调用

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    // TODO: 释放托管状态(托管对象)。
                    //这里应该包含成员对象的dispose
                    //safehandle.Dispose();
                    
                    
                }

                // TODO: 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。
                // TODO: 将大型字段设置为 null。
                //这里是自身管理的一些资源的释放
                CloseFile();
                app = null;

                disposedValue = true;
            }
        }

        //TODO: 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。
         ~Excel()
        {
            // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
            Dispose(false);
        }

        // 添加此代码以正确实现可处置模式。
        void IDisposable.Dispose()
        {
            // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
            Dispose(true);
            // TODO: 如果在以上内容中替代了终结器,则取消注释以下行。
            //如果dispose(false) 段落没有内容,可以补充下面语句,反之得注释掉
            //这个语句目的是取消析构的运行。假设没有内容,也不需要写析构函数了,所以是多此一举
             GC.SuppressFinalize(this);
        }

 

以上是关于safehandle 和析构函数的主要内容,如果未能解决你的问题,请参考以下文章

构造函数和析构函数

c++ 复制构造函数和析构函数

9. 构造函数和析构函数

c ++的构造函数和析构函数汇编[重复]

C语言里面构造函数和析构函数的运用办法

静态分配的构造函数和析构函数顺序