2

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

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

SomeCondition什么时候true,会在调用UnmanagedResource之前从 using 块的末尾处理吗?return在这种情况下将发生的幕后操作顺序是什么?

4

7 回答 7

3

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

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

展开usingfinally阻塞发生在第 1 步和第 2 步之间。

于 2012-08-17T18:27:32.460 回答
2

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

所以实际上这是实际编译的代码:

var ur = new UnmanagedResource()

try
{        
    if( SomeCondition == true ){
        return SomeReturnValue;
    }
}
finally
{
   ur.Dispose();
}
于 2012-08-17T18:27:03.973 回答
1

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

于 2012-08-17T18:27:11.007 回答
1

如果在其中调用 return,使用块的末尾是否会受到影响?

是的。

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

于 2012-08-17T18:27:28.853 回答
1

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

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

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

...

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

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

于 2012-08-17T18:31:11.287 回答
1

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

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

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

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

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

于 2012-08-17T18:33:57.800 回答
1

来自 Brendan Enrick 的博客:-

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

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

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

使用 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;

他的另一个博客展示了一个活生生的例子

于 2012-08-17T18:35:39.013 回答