VB.NETでもアンマネージドリソースを解放したい
はじめに
今回はアンマネージリソースを抱えるクラスの設計の話ではなく、アンマネージリスースを含むクラスを使い終わって破棄するときのお話です。
ちょっと前にこんなコードを見かけました。
Function Hoge() as Integer Dim connection = New SqlConnection("...") connection.Open() ' なんかの処理 Dim result = expression Return result connection.Dispose() End Function
何が悪いのでしょう。 セオリー通りに開いて閉じてます。
- 接続を開いて
- いろいろやりたいことをやって
- 値を返して
- 接続を閉じて・・・?
閉じれてない? 閉じてない!!
Return result
で制御が呼び出し元に戻り、そのままconnection
が指し示すインスタンスは均一なるマトリクスの裂け目の向こうに行ってしまいます。
そしてガベージコレクタによってインスタンスがファイナライザキューにブッ込まれ、ガベージコレクタがインスタンスのファイナライザを呼び出すまでリソースは解放されないままです。
いずれ解放されるならいいじゃないかと思うかもしれませんが、問題はガベージコレクタによってファイナライザが呼び出されるのがいつ分からないということです。すぐに解放されるかもしれないし、逆にアプリケーションが終了するまで解放されないかもしれません。
また、仮に値を返す前にconnection.Dispose()
を呼び出すようにコードを書き直しても、処理の途中で例外はスローされた場合はDispose()
が呼び出されないのでリソースが解放されないままファイナライザで処理されるまでどっかに行ってしまいます。やばいね
Using
どんなことがあっても確実にリソースを解放してやる必要があるのが我々生産性アプリケーション開発者です。
その辺なんかはUsing
ステートメントや例外ハンドラを使えばいい感じになんとかなるのですが、まぁUsing
の使い方等々はmsdnをはじめいろんなところに書いてあるので今更書かないです。
Using ステートメント (Visual Basic) | Microsoft Docs
CloseとDispose
ここからはどっかに根拠があるっていう訳ではなく、弊社の考え的なものも含まれますので間違っててもごめんなさい。 コメントあたりに書いてもらえればなるはやで修正しますのでいい感じにお願いします。
さて、偉大なる指導者Google様の検索候補を見るとClose
とDispose
の違いは何ぞやという検索が多いみたいです。
違いと言いますと
- Dispose:Disposeパターンを用いて不要になったアンマネージリソースを解放する必要があるクラスが
System.IDisposable
を実装し、リソースを解放する時に呼び出すメソッド。実装しておけばUsing
ステートメントを利用できる。普通はこれでアンマネージリソースを解放するよねってやつ。 - Close:特に上のようなインターフェースやルールなどは無いけどAPI設計によりリソースの解放とか出来ることもある。あとはリソースは全く関係ないたまたま
Close
という名前が妥当なメソッド。
かなと思ってます。
ネット上を見てると
とか書いてあることがあります。 確かにそういうクラスも探したらあるのかもしれませんし、そういう風に実装することもできますが、そのような規則があるっていうmsdnは見たことがありません。
例えばSystem.Text.StreamWriter
のClose
メソッドは内部でDispose
を利用しているので呼び出しの意味は等価になりますし、System.Data.SqlClient.SqlConnection
のClose
とDispose
は機能的には同じとされています。
StreamWriter.Close メソッド (System.IO)
SqlConnection.Close メソッド (System.Data.SqlClient)
どうもBCL*1自体の実装もはっきりと一貫性を持って実装されていないのでどのクラスにも適用できる最適な解はないというのが個人的な答えです。 詳細はドキュメントを参考にして個別に対応するしかないと思ってます。
まぁ、ドキュメントを参考にしろで終わるとなんかアレなので、個人的にこんな感じで判断してますよってのをひとつ。
- リソースを解放するために呼び出すメソッドはドキュメントを読んで判断
- でもまぁ
IDisposable
が実装されていれば普通に考えればこれを呼び出せばokなハズだよね Close
を呼び出した後Open
を呼べるのもあるかもしれないけどDispose
を呼び出した後Open
を呼べるのはまず無い- でもまぁ
Close
は大抵Dispose
と同義なことが多いからClose
後にOpen
するのはドキュメントに明示されていない限りやらない- 明示されててもやりたくない
Dispose
後のインスタンスはまず使えない- 使えるのは設計的にどうかと
- 例外をスローする可能性のあるリソースは例外ハンドラや
Using
ステートメントを利用して確実に解放できるようにする
コンパイラや処理系でもさすがにデザインパターンに準じてるかどうかの判断や強制はできないので『まず』とか『基本的に』なんて言葉を付け加えないと怖い人から怒られそうです。 いくらでもデザインパターンやセオリーを無視したクラスを設計できますからね。
余談ですがレガシーVBでは変数にNothing
を代入すればCOMなんかのリソースが解放できたとかなんとかとありますが、VBにおいてはそんなことはないのでその辺を宜しくお願いします。
正確にはファイナライザが呼び出されれば一応解放はされるのですが、上でも書いた通りいつ呼び出されるのかが分からないのでファイナライザにリソース解放を任せるべきではありません。自分で明示的に解放しましょう。
おわり
*1:Base Class Library 標準クラスライブラリ