質問

主にC ++開発者であるため、Javaの RAII(Resource Acquisition Is Initialization) .NETは常に私を悩ませてきました。クリーンアップの責任がクラスライターからそのコンシューマーに移動するという事実( try finally または.NETの using コンストラクト)は著しく劣っているようです。

すべてのオブジェクトがヒープ上にあり、ガベージコレクターが本質的に決定論的な破壊をサポートしないため、JavaではRAIIがサポートされない理由がわかりますが、.NETでは値型( struct )RAIIの(一見)完全な候補者がいます。スタック上に作成された値型には、適切に定義されたスコープがあり、C ++デストラクタセマンティクスを使用できます。ただし、CLRでは、値型にデストラクタを含めることは許可されていません。

ランダム検索では、値の型がボックス化であるという引数が1つ見つかりましたガベージコレクターの管轄下にあるため、その破壊は非決定論的になります。 この議論は十分に強くないと思います。RAIIの利点は、デストラクタを持つ値型をボックス化(またはクラスメンバーとして使用)できないと言うほど十分に大きいと思います。

簡単に言えば私の質問です:RAIIを.NETに導入するために値型を使用できない理由は他にありますか? (または、RAIIの明らかな利点に関する私の議論に欠陥があると思いますか?)

編集:最初の4つの答えがポイントを逃したため、質問を明確に表現していないはずです。 Finalize とその非決定的な特性について知っています using 構造について知っていますが、これら2つのオプションはRAIIに劣ると感じています。 using は、クラスのコンシューマーが覚えておく必要のあるもう1つのことです( using ブロックに StreamReader を入れるのを忘れた人は何人いますか?)。私の質問は言語設計に関する哲学的な質問です。なぜそれがそうであり、改善できるのでしょうか?

たとえば、汎用の決定論的に破壊可能な値型を使用すると、 using および lock キーワードを冗長にすることができます(ライブラリクラスで実現可能):

    public struct Disposer<T> where T : IDisposable
    {
        T val;
        public Disposer(T t) { val = t; }
        public T Value { get { return val; } }
        ~Disposer()  // Currently illegal 
        {
            if (val != default(T))
                val.Dispose();
        }
    }

私はかつて見た適切な引用符で終わることはできませんが、現在その起源を見つけることができません。

  

冷えた死んだ手が範囲外になったとき、あなたは私の決定論的な破壊を受けることができます。 -アノン

役に立ちましたか?

解決

より良いタイトルは、「C#/ VBにRAIIがない理由」です。 C ++ / CLI(Managed C ++であった中絶の進化)には、C ++とまったく同じ意味でRAIIがあります。すべては、他のCLI言語が使用するのと同じファイナライズパターンの単なる構文糖です(C ++ / CLIの管理対象オブジェクトのデストラクタは事実上ファイナライザーです)。

http://blogs.msdn.com /hsutter/archive/2004/07/31/203137.aspx

他のヒント

すばらしい質問と、私を大いに悩ませた質問。 RAIIの利点は非常に異なって認識されているようです。 .NETでの私の経験では、決定論的(または少なくとも信頼性の高い)リソースコレクションの欠如は大きな欠点の1つです。実際、.NETは、明示的な収集を必要とする可能性がある(ただしそうではない可能性がある)管理対象外のリソースを処理するために、アーキテクチャ全体を何度も使用することを私に強制しました。もちろん、これはアーキテクチャ全体をより難しくし、クライアントの注意をより中心的な側面から遠ざけるため、大きな欠点です。

ブライアン・ハリーは、こちらの理論的根拠について素晴らしい投稿をしています。

>

抜粋:

  

確定的なファイナライズと値型(構造)はどうですか?

     

--------------構造体に関する多くの質問を見てきました   デストラクタなど。これは価値があります   コメント。いろいろあります   一部の言語がそうしない理由の問題   それらを持っている。

     

(1)構成-彼らはあなたに与えない   一般的な決定論的寿命   同じ種類の構成の場合   上記の理由。どれか   1つを含む非決定的クラス   それまでデストラクタを呼び出さない   とにかくGCによって確定されました。

     

(2)コンストラクターのコピー-1か所   本当にいいところは   スタックに割り当てられたローカル。彼らは   メソッドにスコープされ、すべてが   すばらしいです。残念ながら、取得するために   これは本当に機能するために、あなたもする必要があります   コピーコンストラクタを追加して呼び出します   インスタンスがコピーされるたび。   これは最もuくて最も   C ++に関する複雑なこと。あなたは終わります   コードを実行する   予期しない場所。それ   言語の問題の束を引き起こします。   一部の言語デザイナーは、   これに近づかないでください。

     

で構造体を作成したとしましょう   デストラクタが、の束を追加しました   行動を起こすための制限   問題に直面して賢明   上記。制限は   次のようなもの:

     

(1)ローカルとしてのみ宣言できます   変数。

     

(2)渡すことしかできません   by-ref

     

(3)割り当てることはできません。   フィールドにアクセスして呼び出すことができます   それらのメソッド。

     

(4)ボックス化できません   それら。

     

(5)使用する際の問題   リフレクション(レイトバインディング)   通常、ボクシングが含まれます。

     

さらに、   しかし、それは良いスタートです。

     

これらのことはどのような用途でしょうか?でしょうか   実際にファイルを作成するか   できるデータベース接続クラス   ローカル変数としてのみ使用されますか?私   誰も本当にそうだとは思わないでください。   代わりに行うことは、作成することです   汎用接続   自動破壊されたラッパーを作成します   スコープ付きローカル変数として使用します。の   発信者はその後、彼らが何を選ぶでしょう   使用したかった。発信者が   決定とそれは完全ではありません   オブジェクト自体にカプセル化されています。   あなたが何かを使用できることを考えると   で提案が出てくるように   いくつかのセクション。

.NETのRAIIの代替はusing-patternであり、慣れればほぼ同様に機能します。

それに最も近いのは、非常に限られたstackalloc演算子です。

検索すると似たようなスレッドがいくつかありますが、基本的に.NETでRAIIを実行するには、IDisposable型を実装して「using」を使用するだけです。確定的廃棄を取得するステートメント。そうすれば、同じイデオムの多くを実装し、少し言葉遣いだけで使用できます。

私見、VB.netとC#が必要とする大きなものは次のとおりです。

  1. a「使用中」」フィールドの宣言。これにより、コンパイラは、このようにタグ付けされたすべてのフィールドを破棄するコードを生成します。デフォルトの動作は、コンパイラがクラスをIDisposableを実装しないようにするか、多くの一般的なIDisposal実装パターンのいずれかのメイン廃棄ルーチンの開始前に廃棄ロジックを挿入するか、属性を使用してそれを指定することです。廃棄物は特定の名前のルーチンに入れる必要があります。
  2. デフォルトの動作(デフォルトの廃棄メソッドを呼び出す)またはカスタムの動作(特定の名前のメソッドを呼び出す)によって、コンストラクターやフィールド初期化子が例外をスローするオブジェクトを決定論的に破棄する手段。
  3. vb.netの場合、すべてのWithEventフィールドをヌルにする自動生成メソッド。

これらはすべて、vb.netではかなり良く、C#ではやや劣りますが、それらに対する一流のサポートは両方の言語を改善します。

finalize()メソッドを使用して、.netおよびjavaでRAIIの形式を実行できます。 finalize()オーバーロードは、クラスがGCによってクリーンアップされる前に呼び出されるため、クラスが絶対に保持してはならないリソース(ミューテックス、ソケット、ファイルハンドルなど)をクリーンアップするために使用できます。しかし、まだ決定論的ではありません。

.NETでは、IDisposableインターフェイスとusingキーワードを使用して、これらの一部を確定的に行うことができますが、これには制限があります(確定的な動作に必要な場合は構造を使用しますが、確定的なメモリの割り当て解除はなく、クラスで自動的に使用されませんなど) )。

そして、はい、RAIIのアイデアを.NETやその他のマネージ言語に導入する場所があると感じていますが、正確なメカニズムは無限に議論される可能性があります。私が見ることができる他の唯一の選択肢は、(メモリだけでなく)任意のリソースのクリーンアップを処理できるGCを導入することですが、リソースを決定的に解放する必要がある場合は問題が発生します。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top