8

次の件についてご意見をお聞かせください。

1つの特定の目的を達成するためのメソッドがあると想像してください。しかし、そのためには、ローカルスコープのオブジェクトの重要な数のサポートが必要であり、それらの多くはを実装していますIDisposable

MSコーディング標準IDisposableでは、メソッドのスコープを「存続」する必要のないローカルオブジェクトを使用する場合(たとえば、返されることも、より長く存続する状態情報に割り当てられることもありませんobject)、構成を使用する必要がありますusing

using問題は、状況によっては、ネストされたブロックの「地獄」を取得できることです。

using (var disposableA = new DisposableObjectA())
{
     using (var disposableB = new DisposableObjectB())
     {
          using (var disposableC = new DisposableObjectC())
          {
               //And so on, you get the idea.
          }
     }
}

使用しているオブジェクトの一部が共通ベースから派生している場合、またはを実装する共通interfaceを実装している場合は、どういうわけかこれを軽減できますIDisposable。もちろん、これには、オブジェクトの真のタイプが必要なときはいつでも、前述のオブジェクトをキャストする必要があるという犠牲が伴います。キャストの量が手に負えない限り、これは実行可能である場合があります。

using (var disposableA = new DisposableObjectA())
{
     using (DisposableBaseObject disposableB = new DisposableObjectB(),
            disposableC = new DisposableObjectC)
     {
          using (var disposableD = new DisposableObjectD())
          {
               //And so on, you get the idea.
          }
     }
}

もう1つのオプションは、ブロックを使用せず、usingブロックを直接実装することtry-catchです。これは次のようになります。

DisposableObjectA disposableA = null;
DisposableObjectB disposableB = null;
DisposableObjectC disposableC = null;
...

try
{
    disposableA = new DisposableObjectA();
    ....
}
finally
{
     if (disposableA != null)
     {
          disposableA.Dispose();
     }

     if (disposableB != null)
     {
          disposableB.Dispose();
     }

     //and so on
}

面白いことに、VSCodeAnalyzerはこのコードに「間違った」というフラグを立てます。すべての可能な実行パスが、スコープ外になる前にすべての使い捨てオブジェクトが破棄されることを保証するわけではないことを通知します。私の意見では決して起こらないはずのオブジェクトが破棄中にスローされた場合にのみ発生することがわかります。発生した場合、通常は何かが本当に混乱しているという兆候があり、おそらくあなたと同じくらい速く優雅に終了する方が良いでしょうアプリ全体からできます。

ですから、問題は、どのアプローチがより好きかということです。ブロックの数に関係なく、ネストされたブロックを使用することが常に望ましいですusingか、または特定の制限を超えると、try-catchブロックを使用する方が良いですか?

4

3 に答える 3

16

たとえば、ステートメントが1つしかない場合は、中括弧は必要ありません。

using (var disposableA = new DisposableObjectA())
using (var disposableB = new DisposableObjectB())
using (var disposableC = new DisposableObjectC())
{
               //And so on, you get the idea.
}

ただし、これは外側のブロックで何が起こっているかに依存しません。

于 2011-06-03T12:50:15.890 に答える
7

usingステートメント(他の多くのステートメントと同様)は必ずしもコードブロックを必要としないが、単一のステートメントである可能性があることを忘れていると思います。最初の例は次のように書くことができます:

using (var disposableA = new DisposableObjectA())
using (var disposableB = new DisposableObjectB())
using (var disposableC = new DisposableObjectC())
{
    //And so on, you get the idea.
}

これで問題が大幅に緩和されると思います。を実装するインスタンスの呼び出しの間に何かを行う必要がある場合は役に立ちませんIDisposable

私は、意味のある他のブロックをネストすることさえします。 foreach例です。

IEnumerable<int> ints = ...;

using (var disposableA = new DisposableObjectA())
using (var disposableB = new DisposableObjectB())
using (var disposableC = new DisposableObjectC())
foreach (int i in ints)
{
    // Work with disposableA, disposableB, disposableC, and i.
}

これが正しくないと通知された場合、VSCodeアナライザーは正しいことに注意してください。

DisposableObjectA disposableA = null;
DisposableObjectB disposableB = null;
DisposableObjectC disposableC = null;
...

try
{
    disposableA = new DisposableObjectA();
    ....
}
finally
{
     if (disposableA != null)
     {
          disposableA.Dispose();
     }

     if (disposableB != null)
     {
          disposableB.Dispose();
     }

     //and so on
}

using積み重ねて使用すると、次のように複数のtry/finallyブロックにネストされます。

DisposableObjectA disposableA = null;
DisposableObjectB disposableB = null;
DisposableObjectC disposableC = null;
...

try
{
    disposableA = new DisposableObjectA();

    try
    {
        disposableB = new DisposableObjectB();

        // Try/catch block with disposableC goes here.
    }
    finally
    {
         if (disposableB != null)
         {
              disposableB.Dispose();
         }    
    }
}
finally
{
     if (disposableA != null)
     {
          disposableA.Dispose();
     }    
}

disposableA.Disposeあなたの例では、実行時にスローされる例外があり、破棄されない(ブロックが終了する)場合、が呼び出されたときにエラーがスローされる場合、その後は閉じられませんdisposableBdisposableCfinallydisposableBdisposableC

于 2011-06-03T12:51:08.367 に答える
1

最初のコードサンプルの唯一の本当の「問題」は、深いネストであり、コードの読み取りと保守が困難になる可能性があります。中括弧を削除することを提案する他の回答の代わりに、最も深くネストされたコードを別の関数にリファクタリングすることで、これを回避することもできます。これにより、使い捨てオブジェクトの作成と廃棄の懸念が実際に使用されることから切り離されます。

using (var a = new DisposableObjectA())
{
    using (var b = new DisposableObjectB())
    {
         using (var c = new DisposableObjectC())
         {
              SomeFunction(a,b,c);
         }
    }
}
于 2011-06-03T12:55:05.540 に答える