如果编译以下代码:
private async Task<int> M()
{
return await Task.FromResult(0);
}
然后反编译它(我使用dotPeek)并检查所有重要的MoveNext
方法,您将看到在开始附近声明了一个bool
变量;dotPeek为我选择了"flag"
bool flag = true;
在这种情况下,您将看到该变量的一个后续消费者,在初始化第一个异步调用之后的默认case语句中:
if (!awaiter.IsCompleted)
{
this.u003Cu003E1__state = 0;
this.u003Cu003Eu__u0024awaiter11 = awaiter;
this.u003Cu003Et__builder.AwaitUnsafeOnCompleted<TaskAwaiter<int>, Program.u003CPu003Ed__10>(ref awaiter, ref this);
flag = false;
return;
}
我已经尝试了六个比我最初的更复杂的例子,它们在中是一致的,只是在退出方法之前赋值给这个变量。换句话说,在我到目前为止尝试过的所有情况下,这个变量不仅永远不会被消耗,而且只是在从方法返回之前立即被赋予一个非初始值——在这个时间点上,赋值定义上是无用的。
作为背景,我很享受通过c# -> JS交叉编译器尝试在Javascript中实现async/await的过程。我在试着理解在什么情况下我需要考虑这面旗帜的效用。从表面上看,它似乎是虚假的,因此我应该忽略它。然而,我想了解为什么c#编译器引入了这个变量——我怀疑有更复杂的表达式以一种有用的方式使用了这个变量。
简洁地说:为什么c#编译器生成这个flag
变量?
问题下面的评论描述了它的用法:
在try-finally块中包装await语句,并在finally块中设置一些变量。我不完全理解IL逻辑在做什么,但我只是快速地看了一下,它看起来像是使用标志变量来检查何时执行finally块内的代码。
伊利安平邹
Stephen Cleary还为感兴趣的读者添加了一些有用的信息。他推荐这个博客系列,尤其是这篇文章。
@IlianPinzon有正确答案。这在Jon Skeet的一篇教育帖子中有更详细的解释。由于您正在编写一个交叉编译器,我强烈建议您阅读整个系列。
斯蒂芬·克利里