[Forward]Sweeping the IDisposable minefield
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:
- All managed objects do NOT need to implement IDisposable. Just let the GC do its thing. You do NOT need to implement IDisposable if
- does NOT directly own any unmanaged resources (e.g. native handles, memory, pipes, etc..)
- does NOT directly own any managed members that implement IDisposable
- does NOT have special cleanup needs that must run on-demand/asap e.g. unsubscribe from notifiers, close DB Connections, etc.
- If you need to implement IDisposable BUT do not own any (direct) unmanaged resources, You should NOT throw in a free finalizer.
- Consider if you can make your type sealed. This simplifies the implementation a great deal.
- 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.
- 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
- 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.
- 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
- 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
- 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.
- 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.
- 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的更多相关文章
- Forward+ Rendering Framework
近几天啃各种新技术时又一个蛋疼的副产品...额,算是把AMD的Forward+ Sample抄了一遍吧. 其实个人感觉这个AMD大肆宣传的Forward+跟Intel很早之前提的Tiled-Based ...
- 通过IEnumerable和IDisposable实现可暂停和取消的任务队列
一般来说,软件中总会有一些长时间的操作,这类操作包括下载文件,转储数据库,或者处理复杂的运算. 一种处理做法是,在主界面上提示正在操作中,有进度条,其他部分不可用.这里带来很大的问题, 使用者不知道到 ...
- IDisposable的另类用法
IDisposable是.Net中一个很重要的接口,一般用来释放非托管资源,我们知道在使用了IDisposable的对象之后一定要调用IDisposable.Dispose()方法,或者使用.Net提 ...
- Sql Server 聚集索引扫描 Scan Direction的两种方式------FORWARD 和 BACKWARD
最近发现一个分页查询存储过程中的的一个SQL语句,当聚集索引列的排序方式不同的时候,效率差别达到数十倍,让我感到非常吃惊 由此引发出来分页查询的情况下对大表做Clustered Scan的时候, 不同 ...
- JAVA常见面试题之Forward和Redirect的区别
用户向服务器发送了一次HTTP请求,该请求可能会经过多个信息资源处理以后才返回给用户,各个信息资源使用请求转发机制相互转发请求,但是用户是感觉不到请求转发的.根据转发方式的不同,可以区分为直接请求转发 ...
- forward和redirect的区别(转)
Redirect Forward 不同的request 不同的对象,但是可以渠道上一个页面的内容 send后面的语句会继续执行,除非return Forward后面的语句不会继续发送给客户端 速度慢 ...
- jsp/servlet 中sendRedirect,include,forward区别
1 sendRedirect response.sendRedirect(); 服务器根据逻辑,发送一个状态码,告诉浏览器重新去请求新的地址,一般来说浏览器会用刚才请求的所有参数重新请求,所以sess ...
- Vector3.forward
这里我要说的就是Vector3.forward ,它等价与 new Vector3(0,0,1):它并不是一个坐标,它是一个标准向量,方向是沿着Z轴向前.这样平移一次的距离就是1米, 如果 Vecto ...
- jsp动作元素之forward指令
forward指令用于将页面响应转发到另外的页面.既可以转发到静态的HTML页面,也可以转发到动态的JSP页面,或者转发到容器中的Servlet. forward指令格式如下: <jsp:for ...
随机推荐
- 开源企业IM-免费企业即时通讯-ENTBOOST V2014.180 Windows版本号正式公布
ENTBOOST,VERSION 2014.180 Linux版本号公布,主要添加企业IM应用集成功能,完好安卓SDK功能及部分BUG修正. 下一版本号公布时间.7月15日.敬请关注. ENTBOOS ...
- HTML5学习笔记(六):CSS基本样式
背景 需要注意:背景的所有属性都不会向下进行继承. 背景色 我们可以设定一个纯色为背景色. p {background-color: red;} a {background-color: #ff000 ...
- SVN四部曲之SVN设置详解深入
想知道不同的设置是干什么用的,你只需将鼠标指针在编辑框/选项框上停留一秒钟...一个帮助提示气泡就会弹出来. 常规设置 图 4.68. 设置对话框,常规设置页面 这个对话框允许你指定自己喜欢的语言,同 ...
- 【DMQ引擎】-DMQ多进程插件引擎服务平台介绍
写过服务端项目的程序员可能都有非常痛苦的经历,服务端经常奔溃,无从查起.的确一个服务端要稳定下来要经过长期的积累,不停的测试,调试,发现问题,解决问题.这个周期可能很长,几个月,甚至以年计算.许多企业 ...
- Mac上把python源文件编译成so文件
把python源文件编译成so文件 前言 实际上属于一种代码混淆/加密的技术,大家知道python的源文件放在那里,大家是都可以看的,不像C语言编译出来可以拿编译后的东西去运行,所以就出现了这种需求. ...
- FFmpeg Basic学习笔记(4)
图像处理 常见的图片格式包括YUV.BMP.JPG.GIF.PNG. 图像的创建 可以使用下面命令从输入源中截取图像 ffmpeg -i input -ss t image.type 从videocl ...
- signal.h中的宏定义SIG_DFL及SIG_IGN
SIG_DFL,SIG_IGN 分别表示无返回值的函数指针,指针值分别是0和1,这两个指针值逻辑上讲是实际程序中不可能出现的函数地址值.SIG_DFL:默认信号处理程序SIG_IGN:忽略信号的处理程 ...
- 初步理解Python进程的信号通讯
Reference: http://www.jb51.net/article/63787.htm 信号的概念 信号(signal)-- 进程之间通讯的方式,是一种软件中断.一个进程一旦接收到信 ...
- 7. 集成学习(Ensemble Learning)Stacking
1. 集成学习(Ensemble Learning)原理 2. 集成学习(Ensemble Learning)Bagging 3. 集成学习(Ensemble Learning)随机森林(Random ...
- tongjiword,write / read file demo
1.tong ji letter demo mport sys def tongjiword(): fi =open(paht,'r') cont=fi.read() wl={} for line i ...