投稿者 魔界の仮面弁士  (社会人) 投稿日時 2012/5/1 21:46:30
一つの目安としては、「有限のリソースあるいは排他利用的なリソースを管理しているかどうか」でしょうか。


> SqlConnectionは明示的にUsingステートメントを使用していますが、
SqlConnection.Close メソッドの説明を読むと、SqlConnection は明示的な切断(Close)を
必要とするクラスである旨の解説があります。接続資源は有限なリソースです。
※ちなみに SqlConnection.Dispose メソッドは、内部で Close を呼び出しています。


> SqlCommandはFinalizeまかせになっています。
SqlCommand は、確かに Dispose を実装しています。しかし実際には、この中では
ほとんど何も行われていません。プリペアドクエリー等で使われる内部キャッシュをクリアする程度の
処理であり、早期解放が必要な資源を後始末しているわけでは無いからです。


> 検索してみましたが、IDisposableインターフェイスを実装しているなら、Disposeよんでおけという感じでした。
個人的には、一般論としては NG と思っているのですが、限定的には「アリ」だと思います。

仮に、IDisposable.Dispose で、何も処理されていないクラスがあった場合、
呼ぼうが呼ぶまいが結果は変わりません。また、解放処理が記述されているクラスなら、
不要になった資源の早期解放は望ましい行為なので、やはり Dispose を呼ぶべきでしょう。
そういう意味においては、Yes と言えなくもありません。

ですが、どんなクラスでも必ず呼ぶべきかというと、必ずしもそうではありません。
当然ながら、「まだ使用されている資源を Dispose するのは NG」だからです。

たとえば System.IO.StreamWriter/StreamReader は、Dispose メソッド内に
「基となる Stream (FileStream とか MemoryStream とか)を閉じる」という実装が
含まれているため、無条件に Close や Dispose を呼ぶわけにもいきません。

'これは OK 
Using stm As New MemoryStream()
    Dim writer As New BinaryWriter(stm)
    writer.Write(UInt16.MaxValue)
    writer.Write(UInt16.MaxValue)

    stm.Flush()
    stm.Position = 0

    Dim reader As New BinaryReader(stm)
    Dim value As Integer = reader.ReadInt32()

    MsgBox(value)
End Using



'これは「ObjectDisposedException」を引き起こす 
Using stm As New MemoryStream()
    Using writer As New BinaryWriter(stm)
        writer.Write(UInt16.MaxValue)
        writer.Write(UInt16.MaxValue)
    End Using
    stm.Flush()
    stm.Position = 0
    Using reader As New BinaryReader(stm)
        Dim value As Integer = reader.ReadInt32()
        MsgBox(value)
    End Using
End Using




> なぜUsingステートメントを使用、不使用と使い分けしているのかがわかりません。

Dispose が省略されるクラスの多くは、System.ComponentModel.Component を
継承したクラスだと思います。その理由は、
・Component 自体は IDisposable であるため、継承クラスも自動的に Using 可能なクラスとなる。
・しかし Component クラスの Dispose 自体には、即時解放を要する処理が
 記述されていない。(Component.Site.Container に対する処理が行われる程度)
・継承先クラスの Dispose では、即時解放を求める処理が追加される可能性があるが、
 少なくとも SqlCommand クラスでは、Dispose 内にてそうした処理が行われていない。
という理由によるものです。

# これとは別に、「本当は必要だが、コードの本質が分かりにくくなることを嫌い、
# サンプルコードでは終了処理などの記述をあえて省略している」というケースも
# あろうけれど、それは Dispose の話に限ったことでは無いので今回は割愛。


ちなみに Dispose が必要な Component 継承クラスとしては、身近なものでは
System.Windows.Forms.Timer クラスがあります。これの処理内容としては、
Timer が稼働中だった場合にタイマーを停止させるというものです。
(タイマー停止と共に、Timer が使っていた OS の資源は解放される)

ちなみに、デザイン時にフォームに Timer を貼っておいた場合、
フォーム終了時に Dispose される仕組みになっています。


結局のところ、Using が必要か否かというのはそれぞれのクラスによって異なりますので、
そのクラスの説明書(ヘルプ、マニュアル、ソースコード等)などを見て判断する必要があります。

たとえば Form クラスなどのように
 ・Show で呼び出した場合は、呼び出し側が Dispose するべきではない
 ・ShowDialog で呼び出した場合は、呼び出し側で Dispose が必要
というケースもありますしね。