在using块中间返回


196

就像是:

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

我认为这不是返回声明的合适位置,是吗?

Answers:


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
我只是要编辑关于您提到的观点的问题。谢谢。
塔法

请帮助我理解为什么这很糟糕。我想将我在辅助函数中使用的Stream返回到另一个用于图像处理的函数。如果执行此操作,似乎将丢弃Stream?
John Shedletsky'3

3
@JohnShedletsky在这种情况下,应使用using来包装函数调用。就像using(Stream x = FuncToReturnStream()){...},而在FuncToReturnStream内部则不使用。
菲利克斯·凯尔

@JohnShedletsky我敢肯定这是因为return语句使该using块的末尾无法被任何代码路径访问。using块的末端需要运行,以便在需要时可以放置对象。
facepalm42

147

很好

您显然在想

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

被盲目地翻译成:

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

诚然,这将是一个问题,并使该using声明变得毫无意义---这就是为什么这不是它的作用。

编译器确保对象在控制离开块之前就已处置-不管它如何离开块。


7
显然我是。
塔法

好答案@詹姆斯·柯伦(James Curran)!但这让我颇为好奇,它被翻译成什么。还是只能在IL中表达?(我以前从未真正尝试阅读过)。
巴特

1
@Bart-我认为这是将返回表达式评估为一个临时变量,然后进行处置,然后返回该临时变量。
ToolmakerSteve

@詹姆斯·柯兰(James Curran)。从上到下,只有您解释了背景中隐藏的内容。非常感谢。
SercanTimoçin19年

@Bart可能已翻译为:最终尝试{...您的代码...} {x.Dispose(); }
Bip901

94

绝对很好-完全没有问题。为什么您认为这是错误的?

using语句只是try / finally块的语法糖,正如Grzenio所说,也可以从try块返回。

将对返回表达式进行求值,然后将执行finally块,然后将返回该方法。


5
詹姆斯·柯伦(James Curran)的回答解释了我的想法。
塔法


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#对象以自定义方式进行处理,例如,WCF客户端是一个使用语句,如上述返回“无法访问已处理的对象”
OzBob

-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();
        }
    }
}

我传递了要作为csv输出的DataTable。随着中间的返回,它正在将所有行写入流中,但是输出的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();
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.