简体   繁体   English

如果在使用块中进行了返回调用,那么幕后操作的顺序是什么?

[英]If a call to return is made in a using block, what is the order of operation behind the scenes?

Does the end of the using block get hit if a return is called inside of it? 如果在其中调用return是否会触发using块的结尾? For example, 例如,

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

When SomeCondition is true , will the UnmanagedResource be disposed from the end of the using block before the return is called? SomeConditiontrueUnmanagedResource是否会在调用return之前从using块的末尾处理? What is the order of operations behind the scenes that would take place in this scenario? 在这种情况下幕后操作的顺序是什么?

I have no idea what "before the return is called" means. 我不知道什么是“在回归之前”的意思。 "Called" is something that happens to functions, and there's no function known as "the return". “被叫”是功能发生的事情,并且没有被称为“回归”的功能。 When the return statement appears in your program, it causes several things to happen: return语句出现在程序中时,会导致以下几种情况发生:

  1. The return value expression is evaluated and stored. 评估并存储返回值表达式。
  2. Execution returns to the caller. 执行返回给调用者。

Unwinding using and finally blocks happens in between steps 1 and 2. 在步骤1和2之间发生usingfinally块的展开。

finally block runs before the control leaves the using block. finally控件在控件离开using块之前运行。

So really this is the code that actually gets compiled: 所以真的这是实际编译的代码:

var ur = new UnmanagedResource()

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

It will be disposed when you leave to block. 当你离开时会被处理掉。 If you dont leave the block at the } but at the return it will be disposed there (so my teacher told me :)) 如果你不离开街区}但是在返回时它会被放置在那里(所以我的老师告诉我:))

Does the end of the using block get hit if a return is called inside of it? 如果在其中调用返回,是否会触发using块的结尾?

Yes. 是。

using is translated into the try/finally sequence, where finally is guaranteed to be executed (in normal circumstances) either there is exception or not. using被转换为try/finally序列, finally 保证执行(在正常情况下)是否存在异常。

The object is disposed prior to the control flow returning to the caller of the method. 在控制流程返回到方法的调用者之前布置对象。

This is detailed in the C# language specification, section 8.9: 这在C#语言规范的第8.9节中有详细说明:

Execution of jump statements is complicated by the presence of intervening try statements. 由于存在干预的try语句,跳转语句的执行变得复杂。 In the absence of such try statements, a jump statement unconditionally transfers control from the jump statement to its target. 在没有这样的try语句的情况下,跳转语句无条件地将控制从跳转语句转移到其目标。 In the presence of such intervening try statements, execution is more complex. 在存在这种干预的try语句时,执行更复杂。 If the jump statement exits one or more try blocks with associated finally blocks, control is initially transferred to the finally block of the innermost try statement. 如果跳转语句退出一个或多个具有关联的finally块的try块,则控制最初被转移到最里面的try语句的finally块。 When and if control reaches the end point of a finally block, control is transferred to the finally block of the next enclosing try statement. 当控制到达finally块的结束点时,控制权转移到下一个封闭的try语句的finally块。 This process is repeated until the finally blocks of all intervening try statements have been executed. 重复此过程,直到执行了所有介入的try语句的finally块。

... ...

finally blocks associated with two try statements are executed before control is transferred to the target of the jump statement. 最后,在将控制转移到跳转语句的目标之前,执行与两个try语句关联的块。

Since a using statement is turned into a try/finally (detailed in 8.13), this means the Dispose call is guaranteed to occur "prior to the return" (rather, prior to control flow jumping to the caller of this method), as return is a "jump statement". 由于using语句变为try / finally(详见8.13),这意味着Dispose调用保证在“返回之前”发生(相反,在控制流跳转到此方法的调用者之前),作为返回是一个“跳跃声明”。

The order of operations you seem to be looking for is: 您似乎正在寻找的操作顺序是:

  1. The return statement is reached. 达到了return声明。
  2. The value to be returned is calculated and stored on the stack to be used when control pops back up from the function. 计算返回的值并将其存储在堆栈中,以便在从该函数弹出控件时使用该值。
  3. The control prepares to leave the using block, thereby executing the implicit finally within the using block which calls the relevant .Dispose() . 控件准备离开using块,从而finally在调用相关.Dispose()using块中执行隐式。
  4. Control leaves the function and the calling function accesses the value on the stack. Control离开函数,调用函数访问堆栈上的值。

Note that sometimes this presents an unexpected result. 请注意,有时这会产生意外结果。 For example, suppose you do something like this: 例如,假设您执行以下操作:

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

In this case, the return value isn't an actual value but a delayed-execution "pointer" of some sort to the resource (I'm sure there's official terminology with which I'm unfamiliar). 在这种情况下,返回值不是实际值,而是某种类型的延迟执行“指针”(我确定有一些我不熟悉的官方术语)。 But that resource is being disposed before that execution takes place. 但是,在执行执行之前,该资源正在被处置。 (It's disposed after return is reached but before the calling function evaluates the returned result.) So you end up with an error attempting to access a disposed resource. (它在达到return之后但在调用函数评估返回的结果之前处理。)因此,您最终会尝试访问已处置的资源时出错。

From Brendan Enrick's blog:- 来自Brendan Enrick的博客: -

While writing some code earlier today I needed to return from within a using statement. 在今天早些时候编写一些代码时,我需要从using语句中返回。 Doing these sorts of things always makes me appreciate the using statement and how wonderful it really is, so I decided to write about it here. 做这些事总是让我欣赏使用声明,它真的很棒,所以我决定在这里写一下。 As many of you know the using statement in C# is a good tool for managing types which will be accessing unmanaged resources. 很多人都知道C#中的using语句是管理将访问非托管资源的类型的好工具。 Some examples of these are SqlConnections, FileReaders, and plenty of other similar types. 其中一些例子是SqlConnections,FileReaders和许多其他类似的类型。 The key to these is that they all implement the IDisposable interface. 这些的关键是它们都实现了IDisposable接口。 This means that they all need to be cleaned up carefully after using them. 这意味着它们在使用后都需要仔细清理。

The using statement is great because it guarantees that the declared object is disposed no matter how the execution completes. using语句很棒,因为无论执行如何完成,它都能保证声明的对象被处理掉。 Whether you reach the end curly brace marking the end of the using statement, throw and exception, or return from a function, the using statement will call the dispose method and clean up the object. 无论是到达结束使用语句,抛出和异常,还是从函数返回的结束大括号,using语句都将调用dispose方法并清理对象。

This was important in my code because I was able to return directly from within the using statement without worrying about whether or not eh dispose method will fire. 这在我的代码中非常重要,因为我能够直接从using语句中返回,而不必担心eh dispose方法是否会触发。 Whenever I use an object which accesses unmanaged resources I always always always put it in a using statement. 每当我使用访问非托管资源的对象时,我总是总是把它放在using语句中。

It is very important to use a using statement, because it will give you this guarantee that the object will be disposed of correctly. 使用using语句非常重要,因为它可以保证对象将被正确处理。 The object's scope will be for the extent of the using statement, and during the scope of the object it will be read-only if defined in the using statement. 对象的作用域将用于using语句的范围,并且在对象的作用域内,如果在using语句中定义,它将是只读的。 This is also very nice, because it will prevent this important object which manages the unmanaged from being modified or reassigned. 这也非常好,因为它会阻止管理非托管的这个重要对象被修改或重新分配。

This is safe to do, because of how great the using statement is. 这样做是安全的,因为using语句有多好。 No matter which return we hit we know the XmlReader will be disposed of correctly. 无论我们点击哪个返回,我们都知道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 他的另一篇博客说明了一个实例

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM