Transcript IDisposable

確実に
IDisposableを成敗する
guicheng
Twitter: @guicheng
http://blogs.wankuma.com/rudicast/
わんくま同盟 東京勉強会 #64
まえおき
COM系ライブラリを正しく使用するには、
IDisposable オブジェクトをいかに成敗するか
がキモ。
– LDAPだったら3段程度、Excelの場合は6段を超
える入れ子状のCOMオブジェクトを生成しなけれ
ばならない。
– IDisposable オブジェクトを討ち漏らすと、ゾンビ
プロセスが残ったりメモリリークの原因になる。
IDisposable オブジェクトを確実に成敗する方
法について考える。
わんくま同盟 東京勉強会 #64
IDisposableを成敗するには
例外が発生しても、それ以前に生成したイン
スタンスを確実に Dispose() する。
– 例外対応の厳密化
後から作ったインスタンスから順に Dispose()
する。
– 子供を殺してから親を殺す
わんくま同盟 東京勉強会 #64
1. 変数でどうにかする
わんくま同盟 東京勉強会 #64
1. 変数でどうにかする
DispClassA dispA_obj = new DispClassA();
DispClassB dispB_obj = dispA_obj.CleateChiled();
// 何か処理
dispB_obj.Dispose();
dispA_obj.Dispose();
わんくま同盟 東京勉強会 #64
1. 変数でどうにかする
メリット
– 単純明快。誰でも理解できる。
デメリット
– 例外を考慮していない。
• リソースの解放漏れがあり得る
– 変数が増えてくるとワケがわからなくなる。
絶対にやってはならない!
わんくま同盟 東京勉強会 #64
2. finalyでDisposeする
わんくま同盟 東京勉強会 #64
2. finalyでDisposeする
DispClassA dispA_obj = null;
DispClassB dispB_obj = null;
try{
dispA_obj = new DispClassA();
dispB_obj = dispA_obj.CreateChiled();
// 何か処理
}finally{
if( dispB_obj != null ){ dispB_obj.Dispose(); }
if( dispA_obj != null ){ dispA_obj.Dispose(); }
}
わんくま同盟 東京勉強会 #64
2. finalyでDisposeする
メリット
– 例外が発生しても確実に成敗できる。
デメリット
– タイプ量が増える。
– 変数が増えてくるとワケがわからなくなる。
– 変数のスコープが広がる
わんくま同盟 東京勉強会 #64
3. usingに任せる
わんくま同盟 東京勉強会 #64
3. usingに任せる
using( DispClassA dispA_obj = new DispClassA() )
using( DispClassB dispB_obj = dispA_obj.CreateChiled() )
{
// 何か処理
}
わんくま同盟 東京勉強会 #64
3. usingに任せる
メリット
– タイプ量が少ない。
– 例外が発生しても確実に成敗できる。
– try-catch を併用することで例外処理も可能。
デメリット
– 複雑な状況ではネストが深くなる
– refやout引数が使えないなど、若干の制限がある
わんくま同盟 東京勉強会 #64
4. Stackに積み込む
わんくま同盟 東京勉強会 #64
4. Stackに積み込む
Stack<IDisposable> disp_stack = new Stack<IDisposable>;
try{
DispClassA dispA_obj = new DispClassA();
disp_stack.push( dispA_obj );
DispClassB dispB_obj = dispA_obj.CreateChiled();
disp_stack.push( dispB_obj );
// 何か処理
} finally {
foreach( IDisposable disp_obj in disp_stack ) {
if( disp_obj != null ) { disp_obj.Dispose(); }
}
}
わんくま同盟 東京勉強会 #64
4. Stackに積み込む
メリット
– 例外が発生しても確実に成敗できる。
– 例外処理ができる。
– ネストが不要。
デメリット
– オブジェクトを生成するたびに、スタックに積み込
まなければならない。
– finallyでの処理が複雑
わんくま同盟 東京勉強会 #64
5. Disposerを定義する
わんくま同盟 東京勉強会 #64
5. Disposerを定義する
class Disposer : IDisposable {
public void Push( IDisposable disp_obj ) {
if( disp_obj != null ) {
_DispStack.Push( disp_obj );
}
}
public void Dispose() {
foreach( IDisposable disp_obj in _DispStack ) {
disp_obj.Dispose();
}
}
Stack<IDisposable> _DispStack = new Stack<IDisposable>();
}
わんくま同盟 東京勉強会 #64
5. Disposerを定義する
using( Disposer disposer = new Disposer() ) {
DispClassA dispA_obj = new DispClassA();
disposer.Push( dispA_obj );
DispClassB dispB_obj = dispA_obj.CreateChiled();
disposer.Push( dispB_obj );
// 何か処理
}
わんくま同盟 東京勉強会 #64
5. Disposerを定義する
メリット
– タイプ量が少ない。
– usingと組み合わせることで、解放処理を気にす
る必要がなくなる。
デメリット
– オブジェクトを生成するたびに、 Disposerに積み
込まなければならない。
わんくま同盟 東京勉強会 #64
6. Generic関数を導入する
わんくま同盟 東京勉強会 #64
6. Generic関数を導入する
class Disposer : IDisposable {
public TYPE Push<TYPE>( TYPE disp_obj )
where TYPE : IDisposable {
if( disp_obj != null ) { _DispStack.Push( disp_obj ); }
return disp_obj;
}
public void Dispose() {
foreach( IDisposable disp_obj in _DispStack ) {
disp_obj.Dispose();
}
}
Stack<IDisposable> _DispStack = new Stack<IDisposable>();
}
わんくま同盟 東京勉強会 #64
6. Generic関数を導入する
using( Disposer disposer = new Disposer() ) {
DispClassA dispA_obj = disposer.Push( new DispClassA() );
DispClassB dispB_obj = disposer.Push( dispA_obj.CreateChiled() );
// 何か処理
}
わんくま同盟 東京勉強会 #64
6. Generic関数を導入する
メリット
– タイプ量がさらに少ない。
– usingと組み合わせることで、解放処理を気にす
る必要がなくなる。
デメリット
– ほとんどない
• 強いて言えば、Pushが面倒なくらい
わんくま同盟 東京勉強会 #64
7. ラッパーを修正する
わんくま同盟 東京勉強会 #64
7. ラッパーを修正する
class DispClassA : IDisposable {
public void Dispose() {
_Disposer.Dispose();
// DispClassAのDispose処理
}
public DispClassB CreateChiled() {
return _Disposer.Push( new DispClassB() );
}
Disposer _Disposer = new Disposer();
}
わんくま同盟 東京勉強会 #64
7. ラッパーを修正する
using(DispClassA dispA_obj = new DispClassA() ) {
DispClassB dispB_obj = dispA_obj.CreateChiled();
// 何か処理
}
わんくま同盟 東京勉強会 #64
7. ラッパーを修正する
メリット
– 実際に使う際には Push すら不要
– 親を殺せば子々孫々に至るまで皆殺しにできる
デメリット
– 普通に使う分には思いつかない
• 枝を切り払うには実装を考える必要がある
わんくま同盟 東京勉強会 #64
まとめ
IDisposable オブジェクトを生成したら、確実
に成敗しなければならない。
using などの便利な機構が用意されているが、
必ずしも万能ではない。
Stack<T> を使用することで、多くの問題を解
決することができる。
Disposerに発展させることで、さらに簡便に扱
うことができる。
– 解放タイミングの自由度が上がるので、応用範囲
が広がる
わんくま同盟 東京勉強会 #64