C#でクラスのリソース開放をする時、普通はDisposeメソッドを実装する。
このDisposeだがあまりC#詳しくない人だとてきとうに実装してしまいがち。
こんな感じ。↓
// ダメなDisposeの実装方法 internal class MySampleClass { private SqlConnection _connection = new SqlConnection(); public void Dispose() { // ここでリソース開放する _connection.Dispose(); _connection = null; } }
この実装でも呼び出し元でクラスを使い終わった後に必ずDisposeを呼び出すなら問題は無い。
ただC#だとusingステートメントというスコープで囲むことで、スコープを外れたときに自動でDisposeを呼び出してくれる便利な機能がある。
この機能を使えるようにしておいた方がよいのでIDisposableインターフェースを実装しておこう。
// 良いDisposeの実装方法 internal class MySampleClass : IDisposable { private SqlConnection _connection = new SqlConnection(); public void Dispose() { // ここでリソース開放する _connection.Dispose(); _connection = null; } }
こう実装しておくことでクラスを使用する際にusingステートメントが使えてDisposeをあえてコールしなくてもよいのでラク。
using (MySampleClass sample = new MySampleClass()) { // 何か処理をする }
マネージコードだけを使っているクラスの場合はこれで十分だが、Win32API等アンマネージコードを使うようなクラスの場合はもっと正しくDisposeを実装する必要がある。
マネージコードについてはusingを使わずDisposeの呼び出しを忘れたとしても、GCが動いた際に自動で開放されるので最悪問題は無い。だがアンマネージコードについてはGCで自動開放されないのでメモリリーク等の原因になる。
そのためこんな感じで実装する必要がある。
// アンマネージドコードを使う場合の良いDisposeの実装方法 internal class MySampleClass : IDisposable { private bool _disposed; private SqlConnection _connection = new SqlConnection(); // ファイナライザ ~MySampleClass() { Dispose(false); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!_disposed) { if (disposing) { //ここでマネージドリソースの開放をする _connection.Dispose(); _connection = null; } // ここでアンマネージドリソースの開放をする _disposed = true; } } }
ファイナライザを実装することでDisposeの呼び出しを忘れた場合でも、クラスの使用が終わった際に強制的にDisposeが呼ばれるようにする。