8

using利便性と安全性の理由から、プールから/へのオブジェクトの割り当てと解放にステートメント を使用したいと思います

public class Resource : IDisposable
{
    public void Dispose()
    {
        ResourcePool.ReleaseResource(this);
    }
}

public class ResourcePool
{
    static Stack<Resource> pool = new Stack<Resource>();

    public static Resource GetResource()
    {
        return pool.Pop();
    }

    public static void ReleaseResource(Resource r)
    {
        pool.Push(r);
    }
}

そしてプールへのアクセス

using (Resource r = ResourcePool.GetResource())
{
     r.DoSomething();
}

using乱用とDispose()スコープ処理に関するトピックをいくつか見つけましたが、それらのすべてに が組み込まれていusing (Blah b = _NEW_ Blah())ます。
ここでは、using スコープを離れた後にオブジェクトを解放するのではなく、プールに保持します。
using ステートメントが単純に単純に展開される場合、これは正常に機能するtry finally Dispose()はずですが、舞台裏でさらに何かが起こっているのでしょうか、または将来の .Net バージョンでこれが機能しなくなる可能性はありますか?

4

6 に答える 6

8

これはまったく乱用ではありません。これは、C# の一般的なスコープ処理イディオムです。たとえば、ADO.NET オブジェクト (接続、ステートメント、クエリ結果) は通常、usingブロックで囲まれていますが、これらのオブジェクトの一部はDisposeメソッド内のプールに解放されます。

using (var conn = new SqlConnection(dbConnectionString)) {
    // conn is visible inside this scope
    ...
} // conn gets released back to its connection pool
于 2013-08-07T18:14:02.603 に答える
3

有効な使い方ですIDisposable

実際、これは .NET でも接続プールが行われる方法です。DBConnectionオブジェクトをusingステートメントでラップして、接続が確実に閉じられ、接続プールに返されるようにします。

TransactionScopeDisposeパターンを使用して未完了のトランザクションをロールバックするクラスの別の例を次に示します。

Dispose メソッドの呼び出しは、トランザクション スコープの終了を示します。

于 2013-08-07T18:14:01.947 に答える
2

using ステートメントが単なる try finally Dispose() に単純に展開される場合、これは正常に機能するはずですが、舞台裏でさらに何かが起こっているのでしょうか、または将来の .Net バージョンでこれが機能しなくなる可能性はありますか?

します。あなたのコードは正常に動作するはずであり、仕様によって引き続き同じように動作することが保証されています。実際、これはかなり一般的です (良い例として、SQL の接続プールを見てください)。

書かれているように、コードの主な問題は、ReleaseResource内で明示的に呼び出すことができusing、パブリック API の一部であるため、プールがリソースを複数回プッシュする可能性があることです。

于 2013-08-07T18:14:22.527 に答える
1

ステートメントの理解usingは正しいです ( tryfinallyDispose)。これがすぐに変わるとは思いません。もしそうなら、非常に多くのものが壊れるでしょう。

あなたが計画していることに必ずしも問題があるわけではありません。Disposeオブジェクトを実際にシャットダウンするのではなく、「完全に機能していない」状態にします。

これが少しでも気になる場合は、通常のDispose実装パターンに準拠するように実装できます。IDisposable基礎となるクラスのすべてのメソッドを実装および公開するラッパー クラスを用意するだけです。ラッパーが破棄されるときは、ラッパーではなく、基になるオブジェクトをプールに入れます。その後、ラッパーがシャットダウンしたと見なすことができますが、ラップされたものはそうではありません。

于 2013-08-07T18:17:15.600 に答える
0

あなたがしていることは、C++ と RAII のようなものです。そして C# では、C++/RAII のイディオムに可能な限り近いものになります。

C# について少しでも知っている Eric Lippert は、IDispose と using ステートメントを C# RAII イディオムとして使用することに断固として反対しています。ここで彼の詳細な回答を参照してください。IDisposable を使用し、例外の安全性のために「範囲指定された動作」を取得する手段として「使用する」ことは虐待的ですか? .

この RAII 方式で使用される IDisposable の問題の一部は、IDisposable を正しく使用するために非常に厳しい要件があることです。私が見た IDisposable を使用するほとんどすべての C# コードは、パターンを正しく実装できません。Joe Duffy は、IDisposable パターンを実装する適切な方法について詳細に説明したブログ投稿を作成しましたhttp://joeduffyblog.com/2005/04/08/dg-update-dispose-finalization-and-resource-management/。Joe の情報は、MSDN で言及されているものよりもはるかに詳細で広範です。Joe は C# についても多少の知識を持っており、そのドキュメントを具体化するのに役立った非常に賢い貢献者がたくさんいました。

クラスを封印するなど、最小限の最小限の IDisposable パターン (RAII などで使用するため) を実装するために簡単なことを行うことができます。MSDN https://msdn.microsoft.com/en-us/library/system.objectdisposedexception%28v=vs.110%29.aspxは素晴らしい概要ですが、Joe の情報には詳細がすべて含まれています。

しかし、IDisposable で回避できないことの 1 つは、その「バイラル」な性質です。IDisposable 自体であるメンバーを保持するクラスは、IDisposable になる必要があります...using(RAII raii = Pool.GetRAII())シナリオの問題ではありませんが、非常に注意する必要があります。

Eric の立場 (その他のほとんどすべてについて私は彼に同意する傾向があります) と、IDisposable パターンを適切に実装する方法についての Joe の 50 ページのエッセイにもかかわらず、そうは言っても、私はそれを C#/RAII イディオムとして自分自身で使用します.

現在、C# に 1) null 非許容参照 (C++、D、Spec# など) と 2) 非常に不変なデータ型 (D や F# など) がある場合のみ [C# で F# の種類の不変を行うことができますが、それは定型文がたくさんあり、正しく理解するのが難しすぎる... 簡単なものを困難にし、困難なものを不可能にする]) および 3) 適切な言語の一部としての契約による設計 (D または Eiffel または Spec# のように、 C# Code Contracts abomination のように)。 ため息 多分C#7。

于 2015-06-10T13:26:35.193 に答える