【问题标题】:If a call to return is made in a using block, what is the order of operation behind the scenes?如果在 using 块中调用 return,那么幕后的操作顺序是什么?
【发布时间】:2012-08-17 18:25:23
【问题描述】:

如果在其中调用return,是否会命中using 块的末尾?例如,

using( var ur = new UnmanagedResource() )
{
  if( SomeCondition == true ){
   return SomeReturnValue;
  }
}

SomeConditiontrue 时,UnmanagedResource 是否会在调用return 之前从 using 块的末尾被释放?在这种情况下会发生的幕后操作顺序是什么?

【问题讨论】:

    标签: c# return using


    【解决方案1】:

    我不知道“在调用返回之前”是什么意思。 “被调用”是函数发生的事情,没有称为“返回”的函数。当return 语句出现在您的程序中时,它会导致发生几件事情:

    1. 计算并存储返回值表达式。
    2. 执行返回给调用者。

    在第 1 步和第 2 步之间展开 usingfinally 块。

    【讨论】:

    • 你认为什么是合适的而不是return is called
    • @TravisJ:我在第 1 步和第 2 步中所说的内容。
    • @TravisJ 查看我的答案 - 语言规范将其列为“控制转移到跳转语句的目标” - 在这种情况下,是相关方法的调用者。
    【解决方案2】:

    finally 块在控件离开 using 块之前运行。

    这就是实际编译的代码:

    var ur = new UnmanagedResource()
    
    try
    {        
        if( SomeCondition == true ){
            return SomeReturnValue;
        }
    }
    finally
    {
       ur.Dispose();
    }
    

    【讨论】:

    • 您的“返回实际执行”的措辞与问题中的措辞一样糟糕。在计算(执行)返回表达式之前,finally 块不会运行。
    【解决方案3】:

    对象在控制流返回到方法的调用者之前被处理。

    这在 C# 语言规范第 8.9 节中有详细说明:

    由于存在中间的 try 语句,跳转语句的执行变得复杂。在没有此类 try 语句的情况下,跳转语句无条件地将控制从跳转语句转移到其目标。在存在这样的中间 try 语句的情况下,执行更加复杂。如果跳转语句退出一个或多个关联 finally 块的 try 块,则控制最初转移到最里面的 try 语句的 finally 块。当控制到达 finally 块的终点时,控制将转移到下一个封闭 try 语句的 finally 块。重复这个过程,直到所有介入的 try 语句的 finally 块都已执行。

    ...

    finally 与两个 try 语句关联的块在控制转移到跳转语句的目标之前执行。

    由于 using 语句被转换为 try/finally(在 8.13 中有详细说明),这意味着 Dispose 调用保证“在返回之前”发生(而是在控制流跳转到 this 的调用者之前)方法),因为 return 是一个“跳转语句”。

    【讨论】:

    • 感谢您的澄清和规范报价!
    • 在控制流跳转到调用者之前,但在返回值表达式被计算之后。
    【解决方案4】:

    它会在你离开阻止时被处理掉。如果您不在 } 处离开街区,但在返回时它将被丢弃在那里(所以我的老师告诉我:))

    【讨论】:

      【解决方案5】:

      如果在内部调用 return,是否会命中 using 块的末尾 呢?

      是的。

      using 被翻译成try/finally 序列,其中finally保证(在正常情况下)无论有没有异常都会被执行。

      【讨论】:

        【解决方案6】:

        您似乎正在寻找的操作顺序是:

        1. 到达return 语句。
        2. 计算要返回的值并将其存储在堆栈中,以便在从函数中弹出控件时使用。
        3. 控件准备离开using块,从而在调用相关.Dispose()using块中执行隐式finally
        4. 控制离开函数,调用函数访问堆栈上的值。

        请注意,有时这会产生意想不到的结果。例如,假设您执行以下操作:

        using (var db = new SomeLinqDataContext())
            return db.Somethings;
        

        在这种情况下,返回值不是实际值,而是某种指向资源的延迟执行“指针”(我确定有我不熟悉的官方术语)。但是该资源是在执行发生之前被处置的。 (它在到达return 之后但在调用函数评估返回的结果之前被释放。)因此,您最终会在尝试访问已释放的资源时出错。

        【讨论】:

          【解决方案7】:

          From Brendan Enrick's blog:-

          今天早些时候在编写一些代码时,我需要从一个 使用语句。做这些事情总是让我 感谢 using 声明以及它的美妙之处,所以我 决定在这里写下来。很多人都知道使用 C# 中的语句是管理类型的好工具 访问非托管资源。其中一些例子是 SqlConnections、FileReaders 和许多其他类似类型。这 这些的关键是它们都实现了 IDisposable 接口。 这意味着它们都需要在使用后仔细清理 他们。

          using 语句很棒,因为它保证了声明的 无论执行如何完成,对象都会被释放。无论你 到达标记 using 语句结束的结束大括号, throw 和异常,或从函数返回,using 语句 将调用 dispose 方法并清理对象。

          这在我的代码中很重要,因为我能够直接返回 从 using 语句中,而不用担心是否 嗯 dispose 方法会触发。每当我使用访问的对象时 非托管资源我总是总是把它放在一个 using 声明。

          使用 using 语句非常重要,因为它会给出 你这保证对象将被正确处理。这 对象的范围将是 using 语句的范围,并且 在对象的范围内,如果在 使用语句。这也很好,因为它可以防止这种情况 管理非托管对象的重要对象被修改或 重新分配。

          这样做是安全的,因为 using 语句非常棒。不 无论我们击中哪个返回,我们都知道 XmlReader 将被处理掉 正确。

          using (XmlReader reader = XmlReader.Create(xmlPath))
          {
              // ... Do some work...
              if (someCase)
                  return 0;
              // ... Do some work...
              if (someOtherCase)
                  return 1;
          }
          return -1;
          

          One more of his blogs illustrates a live example

          【讨论】:

            猜你喜欢
            • 2022-11-29
            • 1970-01-01
            • 2011-01-16
            • 2016-05-02
            • 2018-07-06
            • 2014-12-28
            • 2021-04-14
            • 1970-01-01
            • 2010-10-14
            相关资源
            最近更新 更多