IDisposable has been around since the beginning of .Net.
The basic premise is simple..

Developers dont need to manage memory now. The 'Garbage Collector' takes care of reclaiming memory for you.. However the GC is non-deterministic.. you can't predict when it will embark on a 'collection' of unused objects. So far so good..
However there are cases when you want deterministic cleanup, e.g. if DB connections are a premium, you want to reclaim them as soon as possible. Enter IDisposable.. Simple concept. Introduce a public Dispose method that you can call when you deem fit.

Creating types

Summary:

  1. All managed objects do NOT need to implement IDisposable. Just let the GC do its thing. You do NOT need to implement IDisposable if
    1. does NOT directly own any unmanaged resources (e.g. native handles, memory, pipes, etc..)
    2. does NOT directly own any managed members that implement IDisposable
    3. does NOT have special cleanup needs that must run on-demand/asap e.g. unsubscribe from notifiers, close DB Connections, etc.
  2. If you need to implement IDisposable BUT do not own any (direct) unmanaged resources, You should NOT throw in a free finalizer.
    1. Consider if you can make your type sealed. This simplifies the implementation a great deal.
    2. If you must have subtypes, then implement the version with the virtual Dispose(bool) overload as detailed below. Again, rethink if you can seal the type.
  3. If you directly own unmanaged resources that need cleanup, check for a managed wrapper type that you can use. e.g. a SafeFileHandle. If there is, use it and fall back to 2. Still no finalizer
  4. If you reach here, you need a finalizer. Finalizer pulls in IDisposable. Ensure that you have a deterministic Dispose implementation, that makes the finalizer redundant and avoids the associated performance penalties. Log an error in the finalizer to call your attention to cases where the clients have forgotten to call Dispose. Fix them.
    1. Consider creating a managed wrapper type because finalizers are hard to get right. e.g. SafeMyNativeTypeWrapper. Deriving from SafeHandle is not recommended - better left to experts
    2. Use GC.AddMemoryPressure and its counterpart to 'help' the GC if you are allocating significant amounts of native memory. Similarly manage handles via the HandleCollector class (e.g. GDI handles). See this post for details except I'd move the Remove.. calls into Dispose instead of the finalizer.

Programming against types that implement IDisposable

  1. Limit the scope of disposable types to within a method. Wrap them within a using blockto ensure that Dispose is called (reliably) when control leaves the using block.
  2. If you need to hold on to a disposable type i.e. as a member field, you need to implement IDisposable on the container type. e.g. If A owns B owns C owns D, where D implements IDisposable, then A,B and C need to implement IDisposable as well.
  3. Do not dispose objects that you don't own. e.g. if you obtain a reference to an object from a container (e.g. a MEF container or a Form's controls collection) OR a static/global accessor  you don't own the object. Hence you shouldn't call dispose and break other clients with ObjectDisposedException. Leave it to the container to Dispose it.

The long-winded version (with code snippets)

Para1: Avoid implementing IDisposable unless necessary, most objects don't need it.

If your type doesn't need IDispose. You can stop reading here.

Para2: If you need deterministic cleanup, implement IDisposable (mini).

  • All public members need to check for _isDisposed == true & throw an ObjectDisposedException
  • Dispose can be called multiple times : once again use _isDisposed and ignore all calls except the first one
  • Dispose should not throw exceptions
  • Call Dispose for disposable managed objects that this type owns. Corollary: Dispose will creep all the way up the object graph. e.g. If TypeA contains B contains C contains D and D implements IDisposable: A,B,C need to implement IDisposable.
  • (Managed Memory Leaks from Events) - Unsubscribe from all events that this object has active subscriptions to. Long-lived Publishers can keep short-lived subscribers alive and prevent them from being collected. Try: clearing subscribers from your own events might be a good idea- set event/delegate to null. Try: using the WeakEventManager/WeakReference type
  • Seal your type - inheritance needs the full blown Dispose pattern (later on in this post).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
sealed class MyType : IDisposable
    {
        // other code
 
        private bool _isDisposed;
 
        public void SomeMethod()
        {
            if (_isDisposed)
                throw new ObjectDisposedException();
            // proceed..
        }
 
        public void Dispose()
        {
            if (_isDisposed)
                return;
 
            // cleanup
 
            _isDisposed = true;
        }
    }

Para3: Avoid finalizers unless necessary.

When are finalizers necessary ?

  • When you have directly owned Unmanaged resources that need to be cleaned up AND there isn't a managed wrapper type that has the finalization routine nailed down e.g. a SafeHandle derivation. If you can, you go back to the previous section.

Finalizers

  • slow down the collection process
  • prolong object lifetime - the object moves into the next generation (whose collection is even less frequent. C# in a Nutshell gives a ratio of Gen 0 10:1  Gen 1)
  • are difficult to get right

Finalizers should

  • not block / throw exceptions
  • must execute quickly
  • not reference other finalizable members (their finalizers may have already run)
  • should log / raise a red flag to indicate Dispose was not called.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
sealed class HasUnmanaged : IDisposable
    {
        public void Dispose()
        {
            Dispose(true);
            // prevent object from being promoted to next Gen/finalizer call
            GC.SuppressFinalize(this);
        }
        ~HasUnmanaged()
        {
            LogSomeoneForgotToCallDispose();
            Dispose(false);
        }
 
        private bool _isDisposed;
        private void Dispose(bool isDisposing)
        {
            if (_isDisposed)
                return;
 
            if (isDisposing)
            {
                // dispose of managed resources(can access managed members)
            }
            // release unmanaged resources
            _isDisposed = true;
        }
    }

Para4: Subclassing a Disposable type

If your type cannot be sealed, then it's time to bring out the big guns. Implement the base type as follows

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class BaseType : IDisposable
    {
        Intptr _unmanagedRes = ... // unmanaged resource
        SafeHandle _managedRes = ... // managed resource
    
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        
        ~BaseType()
        {
            Dispose(false);
        }
    
        private bool _isDisposed;
        virtual void Dispose(bool isDisposing)
        {
            if (_isDisposed)
                return;
 
            if (isDisposing)
            {
                // managed resources dispose
                _managedRes.Dispose();
            }
            // unmanaged resource cleanup
            Cleanup(_unmanagedRes);
            // null out big fields if any
            _unmanagedRes = null;
 
            _isDisposed = true;
        }
    }
  • If the derived type has it's own resources that need cleanup, it overrides the virtual member like this
  • If the derived type does not have resources that need cleanup, just inherit from the base. You're done.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class MessyDerived : BaseType
    {
        // ... Derived members
 
        private bool _isDisposed;
        override void Dispose(bool isDisposing)
        {
            try
            {
                if (!_isDisposed)
                {
                    if (isDisposing)
                    {
                        // derived managed resources
                    }
                    // derived unmanaged resources
                    _isDisposed = true;
                }
            }
            finally
            {
                base.Dispose(isDisposing);
            }
        }
    }
 
 
    class SimpleDerived : BaseType
    {
        // ... Derived members
    }

Of course, there will be edge-cases. But for the most part, this should save you a lot of grief
See also - Joe Duffy's post

[Forward]Sweeping the IDisposable minefield的更多相关文章

  1. Forward+ Rendering Framework

    近几天啃各种新技术时又一个蛋疼的副产品...额,算是把AMD的Forward+ Sample抄了一遍吧. 其实个人感觉这个AMD大肆宣传的Forward+跟Intel很早之前提的Tiled-Based ...

  2. 通过IEnumerable和IDisposable实现可暂停和取消的任务队列

    一般来说,软件中总会有一些长时间的操作,这类操作包括下载文件,转储数据库,或者处理复杂的运算. 一种处理做法是,在主界面上提示正在操作中,有进度条,其他部分不可用.这里带来很大的问题, 使用者不知道到 ...

  3. IDisposable的另类用法

    IDisposable是.Net中一个很重要的接口,一般用来释放非托管资源,我们知道在使用了IDisposable的对象之后一定要调用IDisposable.Dispose()方法,或者使用.Net提 ...

  4. Sql Server 聚集索引扫描 Scan Direction的两种方式------FORWARD 和 BACKWARD

    最近发现一个分页查询存储过程中的的一个SQL语句,当聚集索引列的排序方式不同的时候,效率差别达到数十倍,让我感到非常吃惊 由此引发出来分页查询的情况下对大表做Clustered Scan的时候, 不同 ...

  5. JAVA常见面试题之Forward和Redirect的区别

    用户向服务器发送了一次HTTP请求,该请求可能会经过多个信息资源处理以后才返回给用户,各个信息资源使用请求转发机制相互转发请求,但是用户是感觉不到请求转发的.根据转发方式的不同,可以区分为直接请求转发 ...

  6. forward和redirect的区别(转)

    Redirect Forward 不同的request 不同的对象,但是可以渠道上一个页面的内容 send后面的语句会继续执行,除非return Forward后面的语句不会继续发送给客户端 速度慢 ...

  7. jsp/servlet 中sendRedirect,include,forward区别

    1 sendRedirect response.sendRedirect(); 服务器根据逻辑,发送一个状态码,告诉浏览器重新去请求新的地址,一般来说浏览器会用刚才请求的所有参数重新请求,所以sess ...

  8. Vector3.forward

    这里我要说的就是Vector3.forward ,它等价与 new Vector3(0,0,1):它并不是一个坐标,它是一个标准向量,方向是沿着Z轴向前.这样平移一次的距离就是1米, 如果 Vecto ...

  9. jsp动作元素之forward指令

    forward指令用于将页面响应转发到另外的页面.既可以转发到静态的HTML页面,也可以转发到动态的JSP页面,或者转发到容器中的Servlet. forward指令格式如下: <jsp:for ...

随机推荐

  1. [MeetCoder] Count Pairs

    Count Pairs Description You are given n circles centered on Y-aixs. The ith circle’s center is at po ...

  2. Android 开发之修改 app 的字体大小(老人模式)

    新的需求(可参见 微信和QQ改变字体): app 字体不随着系统字体大小变化 app 设置中有设置字体大小的开关,变大以后,整个 app 字体变大. 解决方案:(字体需要采用 dp 为单位,不能使用 ...

  3. Android4.42-Settings源代码分析之蓝牙模块Bluetooth总体实现(总)

    本文为博主原创,转载请注明出处:http://blog.csdn.net/zrf1335348191/article/details/50995466 蓝牙相关代码已在另两篇文章中介绍,有须要的能够查 ...

  4. at org.apache.catalina.webresources.CachedResource.validateResources

    "catalina-exec-659" #5239 daemon prio=5 os_prio=0 tid=0x00007fcba8099800 nid=0x581 waiting ...

  5. bug ,improvements, features jira等信息

    https://issues.apache.org/jira/secure/ReleaseNote.jspa?version=12341764&projectId=12315522 https ...

  6. [Windows Azure] Windows Azure Virtual Network Overview

    Windows Azure Virtual Network Overview 18 out of 33 rated this helpful - Rate this topic Updated: Ap ...

  7. Android 编程下string-array 的使用

    在实际开发中,当数据为固定数据.数据量不是很大.希望很方便的获取到这些数据的时候,可以考虑使用这种低成本的方式来获取预装数据.将想要保存的数据存储到 values 文件夹下的 arrays.xml 文 ...

  8. [DIOCP3/MyBean/QDAC开源项目] DataModule-DB例子基于MyBean的插件实例<三层数据库方案>

    [说明] 这个例子答应大家很久了,一直没有时间弄,现在正式结合MyBean插件可以很方便的在客户端共享操作连接,执行数据库的各项工作,屏蔽了底层的通信解码器编码等工作,直接传递Variant,给了开发 ...

  9. 【Java】Java复习笔记-第四部分

    反射 反射: 在运行时动态分析或使用一个类进行工作. java.lang.Class类:描述类信息的类. 类对象:描述一个类信息的对象,当虚拟机加载类的时候,就会创建这个类的类对象并加载该对象,Cla ...

  10. Android NDK之二:创建NativeActivity

    转:http://blog.csdn.net/xiruanliuwei/article/details/7560914 Android NDK为我们提供了两种方式来实现我们的native activi ...