- PR -

Dispose の意味が未だわからないのですが

投稿者投稿内容
囚人
ぬし
会議室デビュー日: 2005/08/13
投稿数: 1019
投稿日時: 2006-10-27 12:45
Dispose が必要になった経緯を知っていると理解の助けになるかもしれません。

ガベージコレクタがない言語では、メモリ、ファイルハンドル、データベースコネクション、GDIハンドル等あらゆるリソースの管理はプログラマの責任でした。

C++ を例に挙げます。ノーマルな C++ にはガベージコレクタがありません(実装系によっては C++ にガベージコレクタ機能があったりするらしいですが)。なので、メモリの解放、ファイルハンドルの解放、データベースコネクションの解放、あらゆるリソースの解放に気を使ってプログラミングします。

一般的にはクラスが持つリソースは、デストラクタで解放します。そうすると、そのクラスがスタックに保持された時はスコープを抜けた時に必ずデストラクタが呼ばれ、ヒープに保持された時はプログラマが delete と書いたときにデストラクタが呼ばれますので、リソースの解放を完全にコントロールする事ができます。

もちろん、デストラクタだけに任せないで、クラスがファイルハンドルを持っているならば、クラスが Close メンバ関数を用意する場合もあります。

CLR はそう言ったリソース管理の内、メモリの解放「だけ」面倒をみてくれます。それがガベージコレクタであり、メモリは「マネージリソース」と呼ばれるようになりました。

CLR はファイルハンドル、データベースコネクション等、解放のタイミングがシビアなリソースは面倒を見てくれません。それらは「アンマネージドリソース」と呼ばれます。

ガベージコレクタのおかげで、メモリの解放からプログラマは解放されたわけですが、肝心のデストラクタが一体いつ呼ばれるのかが分からなくなりました。これでは、デストラクタにタイミングがシビアなリソースの解放処理を書いても意味がありません。

そこで、クラスがタイミングがシビアなリソースを持っている場合は、Dispose で解放しようと決めました。そういったクラスは IDisposable を使い「自分は解放のタイミングがシビアな何かを持っています」と宣言しているわけです。そうすると、そのクラスを使う側は解放のタイミングをコントロールする事が可能になります。
_________________
囚人のジレンマな日々
R・田中一郎
ぬし
会議室デビュー日: 2005/11/03
投稿数: 979
投稿日時: 2006-10-27 13:14
引用:

渋木宏明(ひどり)さんの書き込み (2006-10-27 11:58) より:
引用:

同様に、ラッピングした場合も、IDisposable を実装して、Dispose を実行させるようにします。



IDisposable.Dispose() を「いつ呼び出すべきか」は状況によって異なります。


確かにそうですね。
上記は、ラッピングとしていますが、継承に近い使い方を前提としたつもりでした。
わかりにくかったですね。ごめんなさい。
_________________
R・田中一郎 -  R.Tanaka.Ichiro’s Blog
ハルシオン
常連さん
会議室デビュー日: 2005/03/29
投稿数: 24
投稿日時: 2006-10-27 23:09
皆さん、親切な回答ありがとうございます。


Hongliangさん

お世話になっております。
このお話はとってもわかりやすいですね!
こういうわかりやすい例に今まで触れることが出来なかったので嬉しいです。
イメージしやすい。。。

引用:


CLR が管理するのは(マネージドなものは)基本的にメモリのみです。
当然のことながらファイルはメモリではないので管理してくれません。
ファイルを開いた人は責任を持って閉じる必要があり、CLR が勝手に閉じてはくれません。




おお!ファイルにDisposeが必要なのはわかりました。
ファイルは確かにオブジェクトじゃないし、ガベージコレクションがアレコレやる範疇のことではない。
だからDisposeしてやらないといけない、と言うことでしょうか。
(間違っていたらどなたかご指摘ください。)

でもそうなってくると、そのファイルを操作した「FileStream クラス」はどうなるのだろうという疑問が沸いてきます。
皆さんも仰っている「IDisposable.Dispose() 」についての部分に関連してくるのでしょうか。


ognacさん

お世話になっております。

引用:


後始末処理の重要性と認識したほうが理解できるかと思います。

Usingで括れば, End Usingのタイミングで Disposeが呼ばれますので, Classが確り作っていれば、後始末を考えなくてすむ分、楽になります。




UsingはC#でしか使えないんですよね?
(VBでも使えるのだろうか…VBだと Try 〜 Finally になるのかな。)
Usingを使用すれば、「End Using」の直後に、明示的にDisposeを記述しなくてもDisposeされるってことですか?
だとするととてつもなく便利かもしれない!!!
ありがとうございます。



R・田中一郎さん

お世話になっております。

引用:


Jittaさんのブログに分かりやすい説明がありますので、ご参考になさって下さい。
http://blogs.wankuma.com/jitta/archive/2006/02/21.aspx




これはまたドンピシャリな記事を見つけていただいてありがとうございます。
確かに「アンマネージリソースとは?」については実は全然わかっていなかったです。
ありがとうございます。


引用:


同様に、ラッピングした場合も、IDisposable を実装して、Dispose を実行させるようにします。




ここら辺からだんだん怪しくなってくるのですが、例えばStreamなんかは、なぜかDisposeが隠蔽されていたりしますよね?
なんでわざわざ隠してる機能を「IDisposable」で「Dispose」するんだろう。

Hongliangさんの例から「ファイル」は「オブジェクト」ではないから「Dispose」してやらなければいけないというのは理解できました。
でも、「Stream」はマネージドリソースなのになぜに「Dispose」してやらなければいけないのだろう。
しかも、「Stream」自体にはDisposeメソッドは存在しないのに、なぜなんでしょう。

ガベージコレクションで回収されるタイミングがいつになるかわからないから、でしょうか。


じゃんぬねっとさん

お世話になっております。

引用:


Stream 系は、この限りでないです。




と、仰るのはどういうことなのでしょうか?
「Dispose」が隠蔽されていることに関係があったりしますか?


渋木宏明(ひどり)さん

お世話になっております。

引用:


また、IDisposable を実装するクラスを継承する場合でも、そのクラスに Dispose(bool disposed) のようなインフラが整備されている場合は、基底クラスが実装する IDisposable.Dispose() を直接呼び出すのではなく、Dispose(bool dispose) を使用するのが正道と思います。




例えば、「DataSetをDisposeする時はそのままDisposeを使用することが望ましく(Disposeメソッドがあるので)、StreamのようなDisposeが隠蔽されているようなクラスはIDisposableでDisposeすることが望ましい」
という理解の仕方で大丈夫でしょうか。

一人でとんちんかんなことを言っているようでしたらご指摘ください。


かるあさん

お世話になっております。

実はご紹介いただいた記事には一度目を通したのですが理解なかなか理解できませんでした。
皆様から頂いた回答と照らし合わせてもういちど解読してみます。
ありがとうございます。


囚人さん

お世話になっております。

> Dispose が必要になった経緯

とても興味深く拝読させていただきました。


引用:


そこで、クラスがタイミングがシビアなリソースを持っている場合は、Dispose で解放しようと決めました。そういったクラスは IDisposable を使い「自分は解放のタイミングがシビアな何かを持っています」と宣言しているわけです。そうすると、そのクラスを使う側は解放のタイミングをコントロールする事が可能になります。




おお!
なんだかここで「Dispose」と「IDisposable」が繋がってきました。
でもやはり「Dispose」が直接触れてはいけないことがある場合についてはなんとなく釈然としません。

少し脱線しますが、C++プログラマの皆さんに比べてなんて自分は楽をしているんだろうと思いました。


皆様から頂いた回答より、取り急ぎ考えを纏めてみました。
何度か読み返させていただき、理解を深められそうな点は理解してまいりたいと思います。

少しまだ疑問も残っておりますが、もう少し自分の中で不明点を整理する必要がありそうです。

後ほど、再度質問させていただきます。



Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2006-10-27 23:42
私のブログエントリで、NyaRuRuさんがコメントしてくださっているとおり、
「わかっているからできる話もある」
ということで。

できるだけ、コメントの方で引用されている NyaRuRuさんのエントリ、囚人さんのエントリも、あわせて読んでいただきたいと思います。
_________________
よこけん
大ベテラン
会議室デビュー日: 2006/01/31
投稿数: 216
投稿日時: 2006-10-28 00:22
引用:
ハルシオンさんの書き込み (2006-10-27 23:09) より:

UsingはC#でしか使えないんですよね?
(VBでも使えるのだろうか…VBだと Try 〜 Finally になるのかな。)
Usingを使用すれば、「End Using」の直後に、明示的にDisposeを記述しなくてもDisposeされるってことですか?


VB2005からは、usingが使えますよ。usingブロックを抜けるタイミングでIDisposable.Disposeを自動で呼び出してくれます。


引用:
ハルシオンさんの書き込み (2006-10-27 23:09) より:

例えばStreamなんかは、なぜかDisposeが隠蔽されていたりしますよね?
なんでわざわざ隠してる機能を「IDisposable」で「Dispose」するんだろう。


StreamのDisposeは隠蔽されてないですね。(.NET 1.1では隠蔽されてたっけ・・・?)
隠蔽している場合は、大抵Closeメソッドがその役割を果たしてくれます。
Closeがある場合、当然、対となるOpenというメソッドがあります。
DisposeもCloseも同等の機能となる場合は、Disposeが隠蔽されることもあります。


引用:
ハルシオンさんの書き込み (2006-10-27 23:09) より:

Hongliangさんの例から「ファイル」は「オブジェクト」ではないから「Dispose」してやらなければいけないというのは理解できました。
でも、「Stream」はマネージドリソースなのになぜに「Dispose」してやらなければいけないのだろう。


Stream系はアンマネージリソースを内部に所持するからです。(そもそもアンマネージリソースそのものにはDisposeメソッドなんてものはありません。)
アンマネージリソースを所持しているのだから、「Streamを使う側」がアンマネージリソースを解放しろと命令する必要があります。


引用:
ハルシオンさんの書き込み (2006-10-27 23:09) より:
引用:


また、IDisposable を実装するクラスを継承する場合でも、そのクラスに Dispose(bool disposed) のようなインフラが整備されている場合は、基底クラスが実装する IDisposable.Dispose() を直接呼び出すのではなく、Dispose(bool dispose) を使用するのが正道と思います。




例えば、「DataSetをDisposeする時はそのままDisposeを使用することが望ましく(Disposeメソッドがあるので)、StreamのようなDisposeが隠蔽されているようなクラスはIDisposableでDisposeすることが望ましい」
という理解の仕方で大丈夫でしょうか。

一人でとんちんかんなことを言っているようでしたらご指摘ください。


クラスを継承させる時の話だと思います。
基本クラスがIDisposableを実装していて、Protected Overridable Overloads Sub Dispose(ByVal disposing As Boolean)メソッドを用意している ( つまり、Dispose-Finalizeパターンを実装している ) なら、
派生クラスではこのメソッドをオーバーライドし、このメソッド内から基本クラスのProtected Overridable Overloads Sub Dispose(ByVal disposing As Boolean)メソッドを呼び出そう、ということですね。
渋木宏明(ひどり)
ぬし
会議室デビュー日: 2004/01/14
投稿数: 1155
お住まい・勤務地: 東京
投稿日時: 2006-10-28 05:46
引用:

ここら辺からだんだん怪しくなってくるのですが、例えばStreamなんかは、なぜかDisposeが隠蔽されていたりしますよね?



この辺はクラス設計者の好みや考え方の問題でしょう。

FileStream が IDisposable.Dispose() を明示的に実装している(=「隠蔽」と呼ぶと変な誤解を招くかもしれません)のは、「IDisposable.Dispose() を実装する必要はあるが、FileStream というクラスの、クラス本来の責務を実行するためのメソッドではない」という意図からだと思います。

FileStream は Close() を実装しているので、正常系だけを考えれば FileStream を使ったファイル操作が終わったら FileStream.Close() を呼び出すのが妥当です。
FileStream.Close() もまた、FileStream が保持するファイルハンドルを解放するので、FileStrem.Close() されているなら、その後に IDisposable.Dispose() を呼び出す必要はありません。

引用:

なんでわざわざ隠してる機能を「IDisposable」で「Dispose」するんだろう。



繰り返しになりますが「隠している」というのは、この場合変な誤解を招く可能性があります。
実際、誤解しているように見えます。

private 就職子などによって「非公開にする」のと、インターフェースメソッドを「明示的に実装する」のでは意味が異なります。

引用:

Hongliangさんの例から「ファイル」は「オブジェクト」ではないから「Dispose」してやらなければいけないというのは理解できました。



「ファイルを Dispose() してやる」のではありません。

「FileStream クラスのインスタンスが保持する、ファイルハンドルというアンマネージリソースを解放するために、FileStream が実装する IDisposable.Dispose() メソッドを呼び出す」のです。

引用:

しかも、「Stream」自体にはDisposeメソッドは存在しないのに、なぜなんでしょう。



「存在していない」わけではありません。
Stream は IDisposable を継承しています。

Stream の派生クラスでは、ネットワーク接続やファイルなど、まず間違いなくアンマネージリソースと呼ばれる資源を扱うため、Steram の段階で IDisposable を継承しているんでしょう。

引用:

例えば、「DataSetをDisposeする時はそのままDisposeを使用することが望ましく(Disposeメソッドがあるので)、StreamのようなDisposeが隠蔽されているようなクラスはIDisposableでDisposeすることが望ましい」
という理解の仕方で大丈夫でしょうか。



全然違います。
既にコメントが付いている様に、継承クラスを作る時の事情について述べています。

「DataSet を使う時」の話ではなく、型付 DataSet のような「DataSet 派生クラスを作る時、IDisposable.Dispose() 関連の実装はどうあるべきか」という話です。


[ メッセージ編集済み 編集者: 渋木宏明(ひどり) 編集日時 2006-10-28 11:04 ]
ぼのぼの
ぬし
会議室デビュー日: 2004/09/16
投稿数: 544
投稿日時: 2006-10-28 19:13
こんばんは。
私も、Disposeに関してはモヤモヤしてることがあるので、
便乗して質問させてください。

例えば、SqlConnection メンバや、StreamWriter メンバには、
パブリック メソッドの中にCloseとDisposeが両方います。
この場合、使用する側の理解としては、以下のどれが正しいのでしょうか?

  1. CloseはOpenと対だから、Openしたなら必ずCloseしろ。Disposeは常に必ずやっとけ。

  2. とりあえず即座に解放したいものはCloseで解放されるから、
    Openしてたら確実にCloseするようになってたら、Disposeは呼ばなくていい。

  3. とりあえず必ずDisposeしとけ。Disposeさえしとけば、
    CloseはDisposeが内部でやってくれるから、呼ばなくていい。

なお、StreamWriterクラスにはOpenメソッドがありませんが、
コンストラクタをOpenの代わりとして解釈しています。
未記入
大ベテラン
会議室デビュー日: 2005/03/12
投稿数: 148
投稿日時: 2006-10-28 19:51
今C#使っていないからどうでもいいっていえばどうでもいいが
usingとかDisposeって面倒だな。

スキルアップ/キャリアアップ(JOB@IT)