我一直在玩弄 Visual Studio 14 CTP 2。此版本的 C# vNext 支持在 finally block 中使用 await
关键字。
我想弄清楚这是如何实现的。我知道这是一个实现细节,在 RTM 发布之前可能会发生变化,但我仍然需要针对此功能进行思考。
为了尝试理解底层编译器生成的代码,我创建了这个示例代码:
private async void button1_Click(object sender, EventArgs e)
{
try
{
}
finally
{
await MyFinallyTest();
}
}
private async Task MyFinallyTest()
{
await Task.Delay(1000);
}
这是编译器生成的类:
[CompilerGenerated]
private sealed class <button1_Click>d__1 : IAsyncStateMachine
{
public int <>1__state;
public Form1 <>4__this;
public object <>7__wrap1;
public int <>7__wrap2;
public AsyncVoidMethodBuilder <>t__builder;
public TaskAwaiter <>u__$awaiter0;
private void MoveNext()
{
int num = this.<>1__state;
try
{
TaskAwaiter awaiter;
switch (num)
{
case 1:
break;
default:
{
this.<>7__wrap1 = null;
this.<>7__wrap2 = 0;
try
{
}
catch (object obj2)
{
this.<>7__wrap1 = obj2;
}
awaiter = this.<>4__this.MyFinallyTest().GetAwaiter();
if (awaiter.IsCompleted)
{
goto Label_0096;
}
this.<>1__state = num = 1;
this.<>u__$awaiter0 = awaiter;
Form1.<button1_Click>d__1 stateMachine = this;
this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter, Form1.<button1_Click>d__1>(ref awaiter, ref stateMachine);
return;
}
}
awaiter = this.<>u__$awaiter0;
this.<>u__$awaiter0 = new TaskAwaiter();
this.<>1__state = num = -1;
Label_0096:
awaiter.GetResult();
awaiter = new TaskAwaiter();
object obj3 = this.<>7__wrap1;
if (obj3 != null)
{
Exception source = obj3 as Exception;
if (source <= null)
{
throw obj3;
}
ExceptionDispatchInfo.Capture(source).Throw();
}
int num1 = this.<>7__wrap2;
this.<>7__wrap1 = null;
}
catch (Exception exception2)
{
this.<>1__state = -2;
this.<>t__builder.SetException(exception2);
return;
}
this.<>1__state = -2;
this.<>t__builder.SetResult();
}
[DebuggerHidden]
private void SetStateMachine(IAsyncStateMachine stateMachine)
{
}
}
据我所知,编译器正在获取 finally
代码块并将其移动到编译器生成的 catch
block 之后。如果我们想在 C# 6.0 之前 await
某些东西,我们必须做的事情类似于我们必须做的事情。
我看到一些我不明白的事情:
编译器正在添加生成的
catch
block (这不是 以catch (object obj2)
的形式出现在我的方法中)和 将其内部object
设置为 obj2 异常。我不明白为什么要这样做。我创建的
finally
block 不再存在。那是不是意味着finally
block 中的任何awaited
代码都不会 开始“享受”我们从实际放置代码中获得的保证 在这样一个街区内?
最佳答案
编译器刚刚开始:
try
{
Foo();
}
finally
{
Bar();
}
变成类似的东西:
Exception caught = null;
try
{
Foo();
}
catch (Exception e)
{
caught = e;
}
Bar();
if (caught != null)
{
throw caught;
}
...但以异步方式。它最终得到相同的结果 - 无论是否抛出异常,您的 finally
block 仍将执行,它只是使用“捕获所有内容然后执行”而不是 finally< 的 IL 版本
.
我建议您考虑在各种情况下执行流程会是什么样子(例如,是否在 try block 中抛出异常)并说服自己在每种情况下结果都是预期的。
关于为什么这不在 C# 5 中,Mads Torgersen 在 C# 6 CTP 文档中写道:
In C# 5.0 we don’t allow the
await
keyword incatch
andfinally
blocks, because we'd somehow convinced ourselves that it wasn’t possible to implement. Now we've figured it out, so apparently it wasn't impossible after all.
关于c# - 在 try-finally block 中等待,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24867683/