【问题标题】:returning in the middle of a using block在 using 块的中间返回
【发布时间】:2010-10-14 08:22:30
【问题描述】:

类似:

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

我认为这里不适合做退货声明,是吗?

【问题讨论】:

    标签: c# dispose idisposable using-statement


    【解决方案1】:

    正如其他几个人指出的那样,这通常不是问题。

    唯一会导致问题的情况是,如果您在 using 语句的中间返回并另外返回 in using 变量。但是话又说回来,即使您没有返回并且只是保留对变量的引用,这也会给您带来问题。

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

    同样糟糕

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

    【讨论】:

    • 刚刚我正要编辑关于你提到的观点的问题。谢谢。
    • 请帮助我理解为什么这很糟糕。我想将我在辅助函数中使用的 Stream 返回到另一个函数以进行图像处理。如果我这样做,似乎 Stream 会被处理掉?
    • @JohnShedletsky 在这种情况下,您的函数调用应该用 using 包装。就像 using(Stream x = FuncToReturnStream()){...} 而没有在 FuncToReturnStream 内部使用。
    • @JohnShedletsky 我确定这是因为return 语句使任何代码路径都无法访问using 块的末尾。 using 块的末尾需要运行,以便可以在需要时释放对象。
    【解决方案2】:

    完全没问题。

    你显然是这么想的

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

    被一味翻译成:

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

    诚然,这将是一个问题,并且会使 using 声明变得毫无意义 --- 这就是它不是的原因。

    编译器确保对象在控制离开块之前被释放——不管它是如何离开块的。

    【讨论】:

    • 很好的答案@James Curran!但这让我很好奇它被翻译成什么。还是只能在 IL 中表达? (我以前从未真正尝试过阅读)。
    • @Bart - 我认为它是将返回表达式评估为一个临时变量,然后进行处置,然后返回临时变量。
    • @James Curran。从上到下,只有你解释了后台发生的事情。非常感谢。
    • @Bart 它可能被翻译成:try { ...your code... } finally { x.Dispose(); }
    【解决方案3】:

    完全没问题 - 完全没有问题。为什么你认为这是错误的?

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

    将计算返回表达式,然后执行 finally 块,然后方法将返回。

    【讨论】:

    • James Curran 的回答解释了我的想法。
    【解决方案4】:

    这将完全正常,就像在try{}finally{}中间返回一样

    【讨论】:

      【解决方案5】:

      这是完全可以接受的。 using 语句确保 IDisposable 对象无论如何都会被释放。

      来自MSDN

      using 语句可确保调用 Dispose,即使在您调用对象上的方法时发生异常也是如此。您可以通过将对象放在 try 块中,然后在 finally 块中调用 Dispose 来获得相同的结果;事实上,编译器就是这样翻译 using 语句的。

      【讨论】:

        【解决方案6】:

        下面的代码显示了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' 已被释放。

        disposed 在 return 语句之后,函数退出之前调用。

        【讨论】:

        • 请注意,某些 C# 对象以自定义方式处理,例如,WCF 客户端是类似上面的 using 语句返回“无法访问处理的对象”
        【解决方案7】:

        也许这不是 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();
        }
        

        【讨论】:

          猜你喜欢
          • 2013-12-01
          • 1970-01-01
          • 1970-01-01
          • 2011-09-15
          • 1970-01-01
          • 1970-01-01
          • 2010-10-25
          • 1970-01-01
          • 2015-10-26
          相关资源
          最近更新 更多