- - PR -
Dispose の意味が未だわからないのですが
投稿者 | 投稿内容 | ||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 2006-10-27 12:45
Dispose が必要になった経緯を知っていると理解の助けになるかもしれません。
ガベージコレクタがない言語では、メモリ、ファイルハンドル、データベースコネクション、GDIハンドル等あらゆるリソースの管理はプログラマの責任でした。 C++ を例に挙げます。ノーマルな C++ にはガベージコレクタがありません(実装系によっては C++ にガベージコレクタ機能があったりするらしいですが)。なので、メモリの解放、ファイルハンドルの解放、データベースコネクションの解放、あらゆるリソースの解放に気を使ってプログラミングします。 一般的にはクラスが持つリソースは、デストラクタで解放します。そうすると、そのクラスがスタックに保持された時はスコープを抜けた時に必ずデストラクタが呼ばれ、ヒープに保持された時はプログラマが delete と書いたときにデストラクタが呼ばれますので、リソースの解放を完全にコントロールする事ができます。 もちろん、デストラクタだけに任せないで、クラスがファイルハンドルを持っているならば、クラスが Close メンバ関数を用意する場合もあります。 CLR はそう言ったリソース管理の内、メモリの解放「だけ」面倒をみてくれます。それがガベージコレクタであり、メモリは「マネージリソース」と呼ばれるようになりました。 CLR はファイルハンドル、データベースコネクション等、解放のタイミングがシビアなリソースは面倒を見てくれません。それらは「アンマネージドリソース」と呼ばれます。 ガベージコレクタのおかげで、メモリの解放からプログラマは解放されたわけですが、肝心のデストラクタが一体いつ呼ばれるのかが分からなくなりました。これでは、デストラクタにタイミングがシビアなリソースの解放処理を書いても意味がありません。 そこで、クラスがタイミングがシビアなリソースを持っている場合は、Dispose で解放しようと決めました。そういったクラスは IDisposable を使い「自分は解放のタイミングがシビアな何かを持っています」と宣言しているわけです。そうすると、そのクラスを使う側は解放のタイミングをコントロールする事が可能になります。 _________________ 囚人のジレンマな日々 | ||||||||||||||||||||||||||||
|
投稿日時: 2006-10-27 13:14
確かにそうですね。 上記は、ラッピングとしていますが、継承に近い使い方を前提としたつもりでした。 わかりにくかったですね。ごめんなさい。 _________________ R・田中一郎 - R.Tanaka.Ichiro’s Blog | ||||||||||||||||||||||||||||
|
投稿日時: 2006-10-27 23:09
皆さん、親切な回答ありがとうございます。
Hongliangさん お世話になっております。 このお話はとってもわかりやすいですね! こういうわかりやすい例に今まで触れることが出来なかったので嬉しいです。 イメージしやすい。。。
おお!ファイルにDisposeが必要なのはわかりました。 ファイルは確かにオブジェクトじゃないし、ガベージコレクションがアレコレやる範疇のことではない。 だからDisposeしてやらないといけない、と言うことでしょうか。 (間違っていたらどなたかご指摘ください。) でもそうなってくると、そのファイルを操作した「FileStream クラス」はどうなるのだろうという疑問が沸いてきます。 皆さんも仰っている「IDisposable.Dispose() 」についての部分に関連してくるのでしょうか。 ognacさん お世話になっております。
UsingはC#でしか使えないんですよね? (VBでも使えるのだろうか…VBだと Try 〜 Finally になるのかな。) Usingを使用すれば、「End Using」の直後に、明示的にDisposeを記述しなくてもDisposeされるってことですか? だとするととてつもなく便利かもしれない!!! ありがとうございます。 R・田中一郎さん お世話になっております。
これはまたドンピシャリな記事を見つけていただいてありがとうございます。 確かに「アンマネージリソースとは?」については実は全然わかっていなかったです。 ありがとうございます。
ここら辺からだんだん怪しくなってくるのですが、例えばStreamなんかは、なぜかDisposeが隠蔽されていたりしますよね? なんでわざわざ隠してる機能を「IDisposable」で「Dispose」するんだろう。 Hongliangさんの例から「ファイル」は「オブジェクト」ではないから「Dispose」してやらなければいけないというのは理解できました。 でも、「Stream」はマネージドリソースなのになぜに「Dispose」してやらなければいけないのだろう。 しかも、「Stream」自体にはDisposeメソッドは存在しないのに、なぜなんでしょう。 ガベージコレクションで回収されるタイミングがいつになるかわからないから、でしょうか。 じゃんぬねっとさん お世話になっております。
と、仰るのはどういうことなのでしょうか? 「Dispose」が隠蔽されていることに関係があったりしますか? 渋木宏明(ひどり)さん お世話になっております。
例えば、「DataSetをDisposeする時はそのままDisposeを使用することが望ましく(Disposeメソッドがあるので)、StreamのようなDisposeが隠蔽されているようなクラスはIDisposableでDisposeすることが望ましい」 という理解の仕方で大丈夫でしょうか。 一人でとんちんかんなことを言っているようでしたらご指摘ください。 かるあさん お世話になっております。 実はご紹介いただいた記事には一度目を通したのですが理解なかなか理解できませんでした。 皆様から頂いた回答と照らし合わせてもういちど解読してみます。 ありがとうございます。 囚人さん お世話になっております。 > Dispose が必要になった経緯 とても興味深く拝読させていただきました。
おお! なんだかここで「Dispose」と「IDisposable」が繋がってきました。 でもやはり「Dispose」が直接触れてはいけないことがある場合についてはなんとなく釈然としません。 少し脱線しますが、C++プログラマの皆さんに比べてなんて自分は楽をしているんだろうと思いました。 皆様から頂いた回答より、取り急ぎ考えを纏めてみました。 何度か読み返させていただき、理解を深められそうな点は理解してまいりたいと思います。 少しまだ疑問も残っておりますが、もう少し自分の中で不明点を整理する必要がありそうです。 後ほど、再度質問させていただきます。 | ||||||||||||||||||||||||||||
|
投稿日時: 2006-10-27 23:42
私のブログエントリで、NyaRuRuさんがコメントしてくださっているとおり、
「わかっているからできる話もある」 ということで。 できるだけ、コメントの方で引用されている NyaRuRuさんのエントリ、囚人さんのエントリも、あわせて読んでいただきたいと思います。 _________________ | ||||||||||||||||||||||||||||
|
投稿日時: 2006-10-28 00:22
VB2005からは、usingが使えますよ。usingブロックを抜けるタイミングでIDisposable.Disposeを自動で呼び出してくれます。
StreamのDisposeは隠蔽されてないですね。(.NET 1.1では隠蔽されてたっけ・・・?) 隠蔽している場合は、大抵Closeメソッドがその役割を果たしてくれます。 Closeがある場合、当然、対となるOpenというメソッドがあります。 DisposeもCloseも同等の機能となる場合は、Disposeが隠蔽されることもあります。
Stream系はアンマネージリソースを内部に所持するからです。(そもそもアンマネージリソースそのものにはDisposeメソッドなんてものはありません。) アンマネージリソースを所持しているのだから、「Streamを使う側」がアンマネージリソースを解放しろと命令する必要があります。
クラスを継承させる時の話だと思います。 基本クラスがIDisposableを実装していて、Protected Overridable Overloads Sub Dispose(ByVal disposing As Boolean)メソッドを用意している ( つまり、Dispose-Finalizeパターンを実装している ) なら、 派生クラスではこのメソッドをオーバーライドし、このメソッド内から基本クラスのProtected Overridable Overloads Sub Dispose(ByVal disposing As Boolean)メソッドを呼び出そう、ということですね。 | ||||||||||||||||||||||||||||
|
投稿日時: 2006-10-28 05:46
この辺はクラス設計者の好みや考え方の問題でしょう。 FileStream が IDisposable.Dispose() を明示的に実装している(=「隠蔽」と呼ぶと変な誤解を招くかもしれません)のは、「IDisposable.Dispose() を実装する必要はあるが、FileStream というクラスの、クラス本来の責務を実行するためのメソッドではない」という意図からだと思います。 FileStream は Close() を実装しているので、正常系だけを考えれば FileStream を使ったファイル操作が終わったら FileStream.Close() を呼び出すのが妥当です。 FileStream.Close() もまた、FileStream が保持するファイルハンドルを解放するので、FileStrem.Close() されているなら、その後に IDisposable.Dispose() を呼び出す必要はありません。
繰り返しになりますが「隠している」というのは、この場合変な誤解を招く可能性があります。 実際、誤解しているように見えます。 private 就職子などによって「非公開にする」のと、インターフェースメソッドを「明示的に実装する」のでは意味が異なります。
「ファイルを Dispose() してやる」のではありません。 「FileStream クラスのインスタンスが保持する、ファイルハンドルというアンマネージリソースを解放するために、FileStream が実装する IDisposable.Dispose() メソッドを呼び出す」のです。
「存在していない」わけではありません。 Stream は IDisposable を継承しています。 Stream の派生クラスでは、ネットワーク接続やファイルなど、まず間違いなくアンマネージリソースと呼ばれる資源を扱うため、Steram の段階で IDisposable を継承しているんでしょう。
全然違います。 既にコメントが付いている様に、継承クラスを作る時の事情について述べています。 「DataSet を使う時」の話ではなく、型付 DataSet のような「DataSet 派生クラスを作る時、IDisposable.Dispose() 関連の実装はどうあるべきか」という話です。 [ メッセージ編集済み 編集者: 渋木宏明(ひどり) 編集日時 2006-10-28 11:04 ] | ||||||||||||||||||||||||||||
|
投稿日時: 2006-10-28 19:13
こんばんは。
私も、Disposeに関してはモヤモヤしてることがあるので、 便乗して質問させてください。 例えば、SqlConnection メンバや、StreamWriter メンバには、 パブリック メソッドの中にCloseとDisposeが両方います。 この場合、使用する側の理解としては、以下のどれが正しいのでしょうか?
なお、StreamWriterクラスにはOpenメソッドがありませんが、 コンストラクタをOpenの代わりとして解釈しています。 | ||||||||||||||||||||||||||||
|
投稿日時: 2006-10-28 19:51
今C#使っていないからどうでもいいっていえばどうでもいいが
usingとかDisposeって面倒だな。 |