猿问

对 IDisposable接口的实现的疑惑

这是MSDN的 接口说明

http://msdn.microsoft.com/zh-cn/library/system.idisposable.aspx

之前也提问过,现在重看,还是有一点迷糊。MSDN例子中的,资源释放代码如下

protected virtual void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called.
            if(!this.disposed)
            {
// 为什么要加这个判断
                if(disposing)
                {
                    // Dispose managed resources.
                    component.Dispose();
                }

                // Call the appropriate methods to clean up
                // unmanaged resources here.
                // If disposing is false,
                // only the following code is executed.
                CloseHandle(handle);
                handle = IntPtr.Zero;

                // Note disposing has been done.
                disposed = true;

            }
        }

这个方法中的中文注释那,为什么要加那个判断。

先谈我的疑惑:

如果用户忘记调用公用的 Dispose方法,也不用担心,因为有析构函数,GC到时会调用析构函数,析构函数会调用 Dispose(false);但是有了 if(disposing)  判断,那不是component的非托管资源无法释放? 看到很多数据库连接释放也是在这个if下,为什么?

 

偶然的你
浏览 507回答 16
16回答

饮歌长啸

但是有了 if(disposing)  判断,那不是component的非托管资源无法释放? 不必担心,因为Component类也有它自己的析构器,Comonent的实例对象被GC时保证了它引用的非托管资源被释放。 那为什么要这么写呢?举个例子你就很容易明白: class Inner : IDisposable { // 包含一些非托管资源 // 实现了 ~Inner() 和 去掉了if判断的 Dispose 方法 } class Wrapper : IDisposable { //这是包含了非托管资源的托管资源 public Inner InnerResouce { get; set; } // 此外本身也有一些非托管资源 // 实现了 ~Wrapper() 和 去掉了if判断的 Dispose 方法 } class Test { Wrapper wrapper = new Wrapper(); void SomeMethod() { var inner = new Inner(); Wrapper anotherWrapper = new Wrapper(); wrapper.InnerResouce = inner; anotherWrapper.InnerResouce = inner; // 在这个方法结束后会发生什么? } } SomeMethod结束后的某个时刻,anotherWrapper被GC,调用了析构器,由于你去掉了if判断,导致Inner.Dispose也被调用了,直接导致wrapper这个实例里的Inner不能正常工作了,这显然不是我们想要的。 由此可见平时被忽略的基础的重要性,其实微软的代码实现只是很简单的遵循了一个很基础的大家都知道的原则,就是职责单一,即:析构器负责释放自身的非托管资源,Dispose方法负责释放所引用到的所有非托管资源,它们两者职责不同,调用时机也不同,Dispose方法永远是程序员显式调用(无论是你用a.Dispose()还是using块),都明确了调用时机。而析构器则无法确定调用时机,因此它没有权利去操纵它成员的非托管资源(因为它无法知道它的成员是否真的需要被释放) 其实你再细心一点,这里Dispose(bool disposing)这个方法还有一个很耐人寻味的地方,就是virtual关键字,这里就不多说了。。

一只甜甜圈

component是托管资源,但是它的component.Dispose();释放的是component里头的非托管资源。 如 文件流, FileStream stream= File.OpenRead("") ;打开了一个文件,要释放非托管资源必须调用Dispose方法,会关闭执行流。 现在我想问的是 为什么会 写在 if(true)下面stream.Dispose(),如果为真才释放的话,用户在没有调用Dispose方法时,那不是stream会一直占用那个文件,因为析构函数也不会调用。

慕婉清6462132

@Qlin:  不调用Dispose是会一直占用着啊,所以才需要用using包着或调用close。

繁星点点滴滴

@向往-SONG:  那 为什么不放在 if(true)外面呢?

红颜莎娜

看到你之前问过类似的问题。 你这里说的非托管资源,即文件资源,是存在这个叫handle的句柄,这里释放的逻辑是在CloseHandle里面去释放的文件资源。 至于要说component中dispose所释放的资源,那是取决于component里面是否有非托管资源需要释放,因为你在外面是不可知的,只有component对象自己最清楚。假设它有使用非托管资源,dispose里面自然就会调用相应的native api去释放资源,如果没有,则可能只是一些实例变量或者静态变量的null化过程。   这里为什么当finalize方法调用的时候传参数为false? 是因为gc在调用对象的finalize方法是没有一定的顺序的,这个过程可能随机的,也就是说,如果该代码是在finalize中调用的时候,不保证component对象的finalize方法是否被调用过(也就不能保证component对象是否存在,这话讲错了,sorry)。然后你担心的在component里面的非托管资源会在它自身的finalize方法中被释放。

至尊宝的传说

不是说 component的finalize方法启动释放。component里的确是有非托管资源, 我是想 使用MyResource类时,不管用户有没有调用MyResource的dispose方法,都可以达到释放MyResource里的非托管资源。但是 放在 if(true)里就不会保证释放了,必须用户调用了。 不只是component,看其他的经典用法都是类似,文件资源、数据库连接等等,都是放在if(true)下,为什么要加这个判断。应该不是 说这个对象不存在了,不存在 大可以判断 component是否等于null。

12345678_0001

@Qlin: 对象a本身的非托管资源的是必须会被释放的。而在finalize方法的时候,对象a所引用的对象b的非托管资源的释放是不应该在a里面调用触发释放的,而是应该被显式(using)或者隐式(finalize)释放的。所以才会有这个disposing开发的

湖上湖

@Ethan轻叹:    对象a所引用的对象b的非托管资源的释放是不应该在a里面调用触发释放的 a.Dispose()方法 不是显示触发释放了b的非托管资源吗? 这是在a中触发的。 为什么a.finalize()方法 不应该触发b的非托管资源,而a.Dispose()方法可以?  

墨色风雨

@Qlin:  freachable queue 不是终止化队列,所有finalize的对象都有一次复活的机会,复活后放到这个队列。 还有就是dispose方法应该被实现成可安全的重复调用,这是约定吧,理论上无论在何时在调用时候都不应该报错的,除非对象不存在。

aluckdog

这个两个类,一个是当前类本身,另外一个是component类,第一个if判断是自身是否需要释放资源,第二个if判断是component类是否需要释放资源,这样做的目的是判断这个component是否被当前类使用,如果正在使用中就不释放这个资源,只释放类本身的资源。
随时随地看视频慕课网APP
我要回答