8

C# の using ステートメントを使用すると問題が発生する可能性があるこのシナリオについて読んでいました。using ステートメントの最後に呼び出された Dispose 関数も例外をスローする場合、using ブロックのスコープ内でスローされた例外は失われる可能性があります。これは、using ステートメントを追加するかどうかを決定する際に、特定のケースで注意を払う必要があることを強調しています。

私は、ストリームと DbConnection から派生したクラスを使用する場合にのみ、using ステートメントを使用する傾向があります。管理されていないリソースをクリーンアップする必要がある場合は、通常、finally ブロックを使用することを好みます。

これは、IDisposable インターフェイスを使用して、タイマーを停止し、Dispose 関数内のレジストリに時間を記録するパフォーマンス タイマーを作成する別の方法です。 http://thebuildingcoder.typepad.com/blog/2010/03/performance-profiling.html

これは IDisposable インターフェイスをうまく利用していますか? リソースをクリーンアップしたり、それ以上のオブジェクトを破棄したりすることはありません。ただし、プロファイリングしているコードを using ステートメントできちんとラップすることで、呼び出し元のコードをクリーンアップする方法を確認できます。

using ステートメントと IDisposable インターフェイスを使用してはいけない場合はありますか? 以前に、using ステートメントで IDisposable またはラッピング コードを実装することで問題が発生したことがありますか?

ありがとう

4

5 に答える 5

5

ドキュメントでそうしないように指示されていない限り、常に使用usingしてください(あなたの例のように)。

メソッドに例外をDisposeスローさせると、むしろそれを使用するポイントが無効になります (しゃれが意図されています)。実装するときはいつでも、オブジェクトの状態に関係なく、例外がスローされないように常に努めています。

PS: これは、WCF の動作を補うための簡単なユーティリティ メソッドです。これにより、Abortが呼び出されたとき以外のすべての実行パスでCloseが呼び出され、エラーが呼び出し元まで伝搬されることが保証されます。

public static void CallSafely<T>(ChannelFactory<T> factory, Action<T> action) where T : class {
    var client = (IClientChannel) factory.CreateChannel();
    bool success = false;
    try {
        action((T) client);
        client.Close();
        success = true;
    } finally {
        if(!success) {
            client.Abort();
        }
    }
}

フレームワークの他の場所で他に面白い動作のケースが見つかった場合は、それらを処理するための同様の戦略を考え出すことができます。

于 2010-07-20T17:46:06.750 に答える
3

一般的な経験則は単純です。クラスが IDisposable を実装する場合は、using. エラーをキャッチする必要がある場合は、try/ catch/を使用finallyしてエラーをキャッチできるようにします。

ただし、いくつかの観察事項があります。

  1. IDisposable を使用すべきではない状況が存在するかどうかを尋ねます。まあ: ほとんどの場合、それを実装する必要はありません。ファイナライザーが作動するまで待つのではなく、タイムリーにリソースを解放したい場合に使用します。

  2. IDisposable が実装されている場合、対応する Dispose メソッドが独自のリソースをクリアし、参照または所有されているオブジェクトをループして、それらのオブジェクトに対して Dispose を呼び出すことを意味する必要があります。また、複数のクリーンアップまたは参照されたオブジェクトが同じことを行い、無限ループが発生するのを防ぐために、Dispose が既に呼び出されているかどうかもフラグする必要があります。ただし、これはすべて、現在のオブジェクトへのすべての参照がなくなることを保証するものではありません。つまり、すべての参照がなくなり、ファイナライザーが起動するまで、オブジェクトはメモリに残ります。

  3. Dispose で例外をスローすることは眉をひそめ、それが発生すると、状態が保証されなくなる可能性があります。厄介な状況です。try/catch/finally を使用して修正でき、finally ブロックで別の try/catch を追加します。しかし、私が言ったように、これはすぐに醜くなります。

  4. 使用は 1 つのことですが、 /usingの使用と混同しないでください。どちらも同じですが、using ステートメントを使用すると、毎回手動で行うのが面倒なスコーピングと null チェックを追加することで作業が楽になります。using ステートメントは次のように変換されます (C# 標準から)。tryfinally

    {
        SomeType withDispose = new SomeType();
        try
        {
             // use withDispose
        }            
        finally 
        {
            if (withDispose != null)
            {
                 ((IDisposable)withDispose).Dispose();
            }
        }
    }
    
  5. オブジェクトを using-block にラップする必要がない場合があります。これらの機会はまれです。子が破棄を必要とする場合に備えて、 IDisposable から継承するインターフェイスから継承していることに気付いたときに発生します。よく使用される例は、すべてのコントロール (フォーム、エディット ボックス、ユーザー コントロールなど) で使用される IComponent です。そして、これらすべてのコントロールを using ステートメントでラップしている人をめったに見かけません。もう一つの有名な例はIEnumerator<T>. その子孫を使用する場合、using ブロックもめったに見られません。

結論

using ステートメントはいたるところで使用し、代替案や除外については慎重に検討してください。usingそれを使用する (使用しない) ことの意味を理解していることを確認し、try/finally とが同等であることに注意してください。何かをキャッチする必要がありますか?try/catch/finally を使用します。

于 2010-07-20T18:11:24.497 に答える
2

私が知っている唯一のケースは、WCF クライアントです。これは、WCF の設計上のバグによるものです - Dispose は決して例外をスローすべきではありません。彼らはそれを逃しました。

于 2010-07-20T18:24:15.773 に答える
2

より大きな問題は、で例外をスローしていると思いますDispose。RAII パターンでは通常、このような状況が発生する可能性があるため、そのようなことを行うべきではないと明示的に述べています。つまり、単に実行を終了する以外に、何かが正しく処理されていない場合の回復パスは何ですか?

また、これは 2 つの try-catch ステートメントで回避できるようです。

try
{
    using(...)
    {
        try
        {
            // Do stuff
        }
        catch(NonDisposeException e)
        {
        }
    }
}
catch(DisposeException e)
{
}

ここで発生する可能性がある唯一の問題DisposeExceptionは、 が と同じかスーパータイプであり、キャッチNonDisposeExceptionから再スローしようとしている場合です。NonDisposeExceptionその場合、DisposeExceptionブロックはそれをキャッチします。したがって、これを確認するには、追加のブール マーカーが必要になる場合があります。

于 2010-07-20T17:49:34.733 に答える
1

一例はIAsyncResult.AsyncWaitHandleプロパティです。賢明なプログラマーは、WaitHandleクラスが実装されていることを認識しIDisposable、自然にそれらを貪欲に破棄しようとします。BCLでのAPMの実装のほとんどが、実際にWaitHandleはプロパティ内の遅延初期化を行うことを除いて。明らかに、その結​​果、プログラマーは必要以上の作業を行いました。

では、内訳はどこにありますか?さて、マイクロソフトはインターフェースを台無しにしましたIAsyncResult。彼らが彼ら自身のアドバイスに従えば、それは使い捨ての資源を保持しているという意味合いIAsyncResultから導き出されたでしょう。IDisposable賢明なプログラマーは、それからただ呼びかけDisposeIAsyncResultその構成要素をどのように処分するのが最善かを決定させます。

IDisposableこれは、廃棄が問題になる可能性がある典型的なフリンジケースの1つです。Disposeジェフリー・リッチターは実際にこの例を使用して、呼び出しは必須ではないと(私の意見では誤って)主張しています。あなたはここで議論を読むことができます。

于 2010-07-20T17:59:53.380 に答える