usingブロックの途中で戻る


196

何かのようなもの:

using (IDisposable disposable = GetSomeDisposable())
{
    //.....
    //......
    return Stg();
}

私はそれがreturnステートメントの適切な場所ではないと思いますか?

回答:


194

他のいくつかが一般的に指摘しているように、これは問題ではありません。

これが問題を引き起こす唯一のケースは、usingステートメントの途中で戻り、さらにin using変数を返す場合です。しかし、繰り返しますが、これは、変数への参照を返さずにそのまま保持したとしても、問題を引き起こします。

using ( var x = new Something() ) { 
  // not a good idea
  return x;
}

同様に悪い

Something y;
using ( var x = new Something() ) {
  y = x;
}

1
あなたが言った点についての私の質問を編集しようとしていました。ありがとう。
tafa 2009年

これがなぜ悪いのか理解してください。ヘルパー関数で使用しているストリームを画像処理用の別の関数に戻したいのですが。これを行うとストリームが破棄されるようです?
John Shedletsky、2015年

3
@JohnShedletskyこの場合、関数呼び出しはusingでラップする必要があります。using(Stream x = FuncToReturnStream()){...}のように、FuncToReturnStream内では使用しません。
Felix Keil

@JohnShedletsky return文がusingブロックの終わりをコードパスからアクセスできないようにするためだと思います。using必要に応じてオブジェクトを破棄できるように、ブロックの最後を実行する必要があります。
facepalm42

147

まったく問題ありません。

あなたはどうやら

using (IDisposable disposable = GetSomeDisposable())
{
    //.....
    //......
    return Stg();
}

盲目的に翻訳されます:

IDisposable disposable = GetSomeDisposable()
//.....
//......
return Stg();
disposable.Dispose();

確かに、これは問題となり、usingステートメントをかなり無意味なものにします---それが理由でそうではありません。

コンパイラは、オブジェクトがブロックを離れる方法に関係なく、制御がブロックを離れる前にオブジェクトが確実に破棄されるようにします。


7
どうやらそうだった。
tafa

すばらしい回答@James Curran!しかし、それが何に翻訳されているのか、私はかなり興味があります。それともILでのみ表現可能ですか?(私はこれまで実際に読んでみたことがありません)。
Bart

1
@Bart-戻り式を評価して一時変数に入れてから、破棄して一時変数を返すと考えています。
ToolmakerSteve 2017年

@ジェームズ・カラン。上からここまで、あなただけがバックグラウンドで何が起こったかを説明しました。どうもありがとう。
SercanTimoçin19年

@Bartそれはおそらく次のように翻訳されます:{... your code ...}最後に{x.Dispose();を試してください。}
Bip901

94

まったく問題ありません。まったく問題ありません。なぜそれが間違っていると思いますか?

usingステートメントは、try / finallyブロックの構文上の糖衣です。Grzenioが言うように、tryブロックから戻ることもできます。

return式が評価され、finallyブロックが実行され、メソッドが返されます。


5
James Curranの答えは私が何を考えていたかを説明しています。
tafa

27

これは、ちょうど真ん中に戻るように、完全にうまくいきます try{}finally{}


18

それはまったく問題ありません。使用した文はIDisposableをオブジェクトがどんな配置されないことが保証されます。

MSDNから:

usingステートメントを使用すると、オブジェクトのメソッドを呼び出しているときに例外が発生した場合でも、Disposeが確実に呼び出されます。オブジェクトをtryブロック内に配置し、finallyブロックでDisposeを呼び出すことで、同じ結果を得ることができます。実際、これは、usingステートメントがコンパイラーによって変換される方法です。


14

以下のコードは、どのように機能しているかを示していますusing

private class TestClass : IDisposable
{
   private readonly string id;

   public TestClass(string id)
   {
      Console.WriteLine("'{0}' is created.", id);
      this.id = id;
   }

   public void Dispose()
   {
      Console.WriteLine("'{0}' is disposed.", id);
   }

   public override string ToString()
   {
      return id;
   }
}

private static TestClass TestUsingClose()
{
   using (var t1 = new TestClass("t1"))
   {
      using (var t2 = new TestClass("t2"))
      {
         using (var t3 = new TestClass("t3"))
         {
            return new TestClass(String.Format("Created from {0}, {1}, {2}", t1, t2, t3));
         }
      }
   }
}

[TestMethod]
public void Test()
{
   Assert.AreEqual("Created from t1, t2, t3", TestUsingClose().ToString());
}

出力:

「t1」が作成されます。
「t2」が作成されます。
「t3」が作成されます。
「t1、t2、t3から作成」が作成されます。
't3'は破棄されます。
't2'は破棄されます。
't1'は破棄されます。

破棄は、returnステートメントの後、関数の終了前に呼び出されます。


1
一部のC#オブジェクトはカスタムの方法で
破棄

-4

たぶん、これが許容できるということは100%真実ではありません...

を使用してネストし、ネストされたものから戻る場合は、安全ではない可能性があります。

これを例にとります:

using (var memoryStream = new MemoryStream())
{
    using (var textwriter = new StreamWriter(memoryStream))
    {
        using (var csv = new CsvWriter(textwriter))
        {
            //..write some stuff to the stream using the CsvWriter
            return memoryStream.ToArray();
        }
    }
}

DataTableを渡してcsvとして出力していました。途中で戻ると、すべての行がストリームに書き込まれていましたが、出力されたcsvには常に行(またはバッファーのサイズによっては複数)がありませんでした。これにより、何かが適切に閉じられていなかったことがわかりました。

正しい方法は、以前のすべての使用が適切に破棄されていることを確認することです。

using (var memoryStream = new MemoryStream())
{
    using (var textwriter = new StreamWriter(memoryStream))
    {
        using (var csv = new CsvWriter(textwriter))
        {
            //..write some stuff to the stream using the CsvWriter
        }
    }

    return memoryStream.ToArray();
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.