[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 ...
随机推荐
- android sqlite3:数据库操作
1. 修改表名: alter table notification rename to notification_test 2. 修改数据库名称: 3. 复制一个表的数据到另外一个表中(表的字段一致) ...
- PHP数组排序sort、asort与ksort用法
分享下PHP数组排序之sort.asort与ksort用法,实例中简单示范了sort.asort与ksort的用法,并备有注释加以详细说明. PHP数组排序中sort.asort与ksort的用法. ...
- [na]数据链路层&网络层协议小结截图版
ip层:分组选路 tcp:端到端的通信 中间系统没必要有应用程序,分组选路即可 应用程序中隐藏所有的物理细节. 语音肯定用udp linux主版本 次版本号 修订号 次版本为偶数说明是稳定版.奇数是开 ...
- markModified声明要修改的数组字段
更新一个文档的字段的时候,如果该字段的类型是数组类型,则必须在更新保存前声明一下这个数组字段要被修改,否则这个数组字段的值不会被修改.如 article.markModified('categorys ...
- Ubantu 使用extundelete恢复数据
所以在维护系统的时候,要慎之又慎,但是有时难免会出现数据被误删除的情况,在这个时候改如何快速.有效地恢复数据呢?本文我们就来介绍一下Linux系统下常用的几个数据恢复工具. 一.如何使用“rm -rf ...
- linux命令(45):去掉 所有文件中的空行
方法一:利用grep grep -v '^\s*$' test.txt 注:-v表示将匹配的结果进行反转,正则表达式匹配空行.(空行可包括空格符制表符等空白字符) 方法二:利用sed sed '/^\ ...
- iOS开发transform的使用
// // ViewController.m // 18-transform的使用 #import "ViewController.h" @interface ViewCont ...
- (转)Go和HTTPS
转自:http://studygolang.com/articles/2946 Go和HTTPS 2015-04-30 bigwhite 阅读 5688 次 4 人喜欢 3 条评论 收 ...
- 【转帖】(一)unity4.6Ugui中文教程文档-------概要
原帖至上,移步请戳:(一)unity4.6Ugui中文教程文档-------概要 unity4.6中的一个重要的升级就是GUI ,也把它称为UGUI ,废话我不多说,大家可以百度了解一下. 虽然现在处 ...
- CentOS 7修改管理员密码
之前安装CentOS 7的时候随便输入了一个密码,现在使用需要root权限,只能重置密码了.下面是处理方法:(进入单用户模式修改root密码为例) 在启动菜单中,按e编辑所选启动项 一页内容,感觉挺陌 ...