C# 在 try { return x; 中真正发生了什么?} 最后{ x = null; } 陈述?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/421797/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What really happens in a try { return x; } finally { x = null; } statement?
提问by Dmitri Nesteruk
I saw this tip in another question and was wondering if someone could explain to me how on earth this works?
我在另一个问题中看到了这个提示,想知道是否有人可以向我解释这到底是如何工作的?
try { return x; } finally { x = null; }
I mean, does the finally
clause really execute afterthe return
statement? How thread-unsafe is this code? Can you think of any additional hackery that can be done w.r.t. this try-finally
hack?
我的意思是,该finally
条款真正执行后的return
声明?这段代码有多线程不安全?你能想到任何额外的hackery可以通过这个try-finally
hack来完成吗?
采纳答案by Marc Gravell
No - at the IL level you can't return from inside an exception-handled block. It essentially stores it in a variable and returns afterwards
不 - 在 IL 级别,您不能从异常处理块内部返回。它本质上将它存储在一个变量中并随后返回
i.e. similar to:
即类似于:
int tmp;
try {
tmp = ...
} finally {
...
}
return tmp;
for example (using reflector):
例如(使用反射器):
static int Test() {
try {
return SomeNumber();
} finally {
Foo();
}
}
compiles to:
编译为:
.method private hidebysig static int32 Test() cil managed
{
.maxstack 1
.locals init (
[0] int32 CSprivate static int Test()
{
int CSusing System;
class Test
{
static string x;
static void Main()
{
Console.WriteLine(Method());
Console.WriteLine(x);
}
static string Method()
{
try
{
x = "try";
return x;
}
finally
{
x = "finally";
}
}
}
00;
try
{
CSclass Test {
public static Exception AnException() {
Exception ex = new Exception("Me");
try {
return ex;
} finally {
// Reference unchanged, Local variable changed
ex = new Exception("Not Me");
}
}
}
00 = SomeNumber();
}
finally
{
Foo();
}
return CSclass Test {
public static Exception AnException() {
Exception ex = new Exception("Me");
Exception CSclass Test2 {
public static System.IO.MemoryStream BadStream(byte[] buffer) {
System.IO.MemoryStream ms = new System.IO.MemoryStream(buffer);
try {
return ms;
} finally {
// Reference unchanged, Referenced Object changed
ms.Dispose();
}
}
}
00 = null;
try {
CSclass ByRefTests {
public static int One(out int i) {
try {
i = 1;
return i;
} finally {
// Return value unchanged, Store new value referenced variable
i = 1000;
}
}
public static int Two(ref int i) {
try {
i = 2;
return i;
} finally {
// Return value unchanged, Store new value referenced variable
i = 2000;
}
}
public static int Three(out int i) {
try {
return 3;
} finally {
// This is not a compile error!
// Return value unchanged, Store new value referenced variable
i = 3000;
}
}
}
00 = ex;
} finally {
// Reference unchanged, Local variable changed
ex = new Exception("Not Me");
}
return CS##代码##00;
}
}
00;
}
00)
L_0000: call int32 Program::SomeNumber()
L_0005: stloc.0
L_0006: leave.s L_000e
L_0008: call void Program::Foo()
L_000d: endfinally
L_000e: ldloc.0
L_000f: ret
.try L_0000 to L_0008 finally handler L_0008 to L_000e
}
This basically declares a local variable (CS$1$0000
), places the value into the variable (inside the handled block), then after exiting the block loads the variable, then returns it. Reflector renders this as:
这基本上声明了一个局部变量 ( CS$1$0000
),将值放入变量中(在处理的块内),然后在退出块后加载变量,然后返回它。反射器将其呈现为:
回答by Otávio Décio
The finally clause executes after the return statement but before actually returning from the function. It has little to do with thread safety, I think. It is not a hack - the finally is guaranteed to always run no matter what you do in your try block or your catch block.
finally 子句在 return 语句之后但实际从函数返回之前执行。我认为它与线程安全关系不大。这不是黑客 - 无论您在 try 块或 catch 块中做什么, finally 都保证始终运行。
回答by casperOne
If x
is a local variable, I don't see the point, as x
will be effectively set to null anyway when the method is exited and the value of the return value is not null (since it was placed in the register before the call to set x
to null).
如果x
是局部变量,我不明白这一点,因为x
当方法退出并且返回值的值不为空时,无论如何都会有效地设置为空(因为它在调用 set 之前被放置在寄存器中x
为空)。
I can only see doing this happening if you want to guarantee the change of the value of a field upon return (and after the return value is determined).
如果您想保证返回时(以及确定返回值之后)字段值的更改,我只能看到这样做会发生。
回答by Jon Skeet
The finally statement is executed, but the return value isn't affected. The execution order is:
finally 语句被执行,但返回值不受影响。执行顺序为:
- Code before return statement is executed
- Expression in return statement is evaluated
- finally block is executed
- Result evaluated in step 2 is returned
- 执行return语句前的代码
- 计算 return 语句中的表达式
- finally 块被执行
- 返回在步骤 2 中评估的结果
Here's a short program to demonstrate:
这是一个简短的程序来演示:
##代码##This prints "try" (because that's what's returned) and then "finally" because that's the new value of x.
这将打印“try”(因为这是返回的内容)然后“finally”,因为这是 x 的新值。
Of course, if we're returning a reference to a mutable object (e.g. a StringBuilder) then any changes made to the object in the finally block will be visible on return - this hasn't affected the return value itself (which is just a reference).
当然,如果我们返回对可变对象(例如 StringBuilder)的引用,那么在 finally 块中对该对象所做的任何更改都将在返回时可见 - 这并没有影响返回值本身(这只是一个参考)。
回答by Arkaine55
Adding onto the answers given by Marc Gravell and Jon Skeet, it is important to note objects and other reference types behave similarly when returned but do have some differences.
除了 Marc Gravell 和 Jon Skeet 给出的答案之外,重要的是要注意对象和其他引用类型在返回时的行为相似但确实存在一些差异。
The "What" that gets returned follows the same logic as simple types:
返回的“What”遵循与简单类型相同的逻辑:
##代码##The reference that is being returned has already been evaluated before the local variable is assigned a new reference in the finally block.
在 finally 块中为局部变量分配新引用之前,已经评估了正在返回的引用。
The execution is essentially:
执行本质上是:
##代码##The difference is it would still be possible to modify mutable types using the properties/methods of the object which can result in unexpected behaviors if you are not careful.
不同之处在于仍然可以使用对象的属性/方法修改可变类型,如果您不小心,这可能会导致意外行为。
##代码##A second thing to consider about try-return-finally is that parameters passed "by reference" can still be modified after the return. Only the return valuehas been evaluated and is stored in a temporary variable waiting to be returned, any other variables are still modified the normal way. The contract of an out parameter can even go unfulfilled until the finally block this way.
关于 try-return-finally 需要考虑的第二件事是“通过引用”传递的参数在返回后仍然可以修改。只有返回值被评估并存储在一个等待返回的临时变量中,任何其他变量仍然以正常方式修改。一个 out 参数的契约甚至可以在最终以这种方式阻止之前无法实现。
##代码##Like any other flow construct "try-return-finally" has its place and can allow for cleaner looking code than writing the structure it actuallycompiles to. But it must be used carefully to avoid gotcha's.
像任何其他流构造一样,“try-return-finally”也有它的位置,并且可以允许比编写它实际编译成的结构更清晰的代码。但必须谨慎使用以避免陷入困境。