c# - 编译器如何将返回值转换为返回任务在异步方法中?
问题描述
我设计了以下方法来创建记录。
public Task<Guid> NotAwaited()
{
Account account = new Account();
Context.Accounts.Add(account);
Context.SaveChangesAsync();
return new Task<Guid>(() => account.Id);
}
然后,我意识到在归还 guid 时存在保存未完成的风险。所以我添加了await
,这需要我用 . 装饰方法签名async
。在那之后,我收到一个错误,要求使用更简单的语法来返回返回的内容,就像这样。
public async Task<Guid> Awaited()
{
Account account = new Account();
Context.Accounts.Add(account);
await Context.SaveChangesAsync();
return account.Id;
}
我知道该account.Id
部分以某种方式转换为任务。我只是不确定如何。感觉就像它是黑魔法(我理解它不是)。
有隐式转换吗?还是我仍然不正确地执行异步调用?
解决方案
感觉就像是黑魔法
恐怕已经到了与魔法无法区分的程度了。
您编写 C# 代码,然后编译器将其拆分为一起运行的片段,并创建一个状态机,该状态机在每次等待的异步任务完成时“向前移动”。如果您调试代码,调试器知道如何在调试器中表示“局部变量”(实际上可能是状态机类型上的实例成员)并映射到原始源代码的行。
因此,对于您的情况,代码可能类似于(通过 sharplab 创建,请参阅此要点):
[AsyncStateMachine(typeof(<Awaited>d__0))]
public Task<Guid> Awaited()
{
<Awaited>d__0 stateMachine = default(<Awaited>d__0);
stateMachine.<>t__builder = AsyncTaskMethodBuilder<Guid>.Create();
stateMachine.<>1__state = -1;
AsyncTaskMethodBuilder<Guid> <>t__builder = stateMachine.<>t__builder;
<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
[StructLayout(LayoutKind.Auto)]
[CompilerGenerated]
private struct <Awaited>d__0 : IAsyncStateMachine
{
public int <>1__state;
public AsyncTaskMethodBuilder<Guid> <>t__builder;
private Account <account>5__2;
private TaskAwaiter <>u__1;
private void MoveNext()
{
int num = <>1__state;
Guid id;
try
{
TaskAwaiter awaiter;
if (num != 0)
{
<account>5__2 = new Account();
Context.Accounts.Add(<account>5__2);
awaiter = Context.SaveChangesAsync().GetAwaiter();
if (!awaiter.IsCompleted)
{
num = (<>1__state = 0);
<>u__1 = awaiter;
<>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
return;
}
}
else
{
awaiter = <>u__1;
<>u__1 = default(TaskAwaiter);
num = (<>1__state = -1);
}
awaiter.GetResult();
id = <account>5__2.Id;
}
catch (Exception exception)
{
<>1__state = -2;
<>t__builder.SetException(exception);
return;
}
<>1__state = -2;
<>t__builder.SetResult(id);
}
void IAsyncStateMachine.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
this.MoveNext();
}
[DebuggerHidden]
private void SetStateMachine(IAsyncStateMachine stateMachine)
{
<>t__builder.SetStateMachine(stateMachine);
}
void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
{
//ILSpy generated this explicit interface implementation from .override directive in SetStateMachine
this.SetStateMachine(stateMachine);
}
}
您可以看到SaveChangesAsync()
被调用方位于与访问 Id 属性的位置不同的逻辑分支中。局部变量account
现在是<account>5__2
生成的结构上的字段。使用的标识符名称实际上都不是有效的 C# 标识符,但在编译为的底层 IL 语言中是有效的,上面的代码是实际生成的代码的反编译 C#-ish 表示。
对该方法的调用Awaited()
实际上将创建“隐藏”<Awaited>d__0
结构的新实例(在调试模式下,它将是 aclass
而不是 astruct
以支持编辑和继续)并使用异步基础结构的类型来连接此状态机并运行它.
MoveNext()
在启动状态机时调用,而且每次等待的任务完成(作为延续)时都会调用。您可以看到最后一部分将结果设置为id
值,这基本上是您的return
声明。
值得一提的是,在大多数将异常包装到任务结果中的代码周围还有一个 try-catch - 因此您可以throw
在您的代码(或从您的代码中调用的代码)中创建一个失败的任务而不是一个调度方法的异步部分时未处理的异常。
推荐阅读
- javascript - 在 mlab 中允许使用 RESTful DELETE 方法
- matlab - 在matlab中的一张图上绘制两个带有时间序列图的数据集
- python - keras.Sequential() 的“input_shape”格式是什么?
- c# - 任务可靠性(或者我应该做其他事情吗?)
- mallet - MALLET 中的主题分配
- delphi - 如何在不编辑所有表单的情况下巧妙地在所有 FormCreate 事件中添加一行代码?
- html - HTML CSS 打印出 POS 大小?
- c++ - 如何将 asmjit 嵌入到自己的 C++ 项目中?
- gradle - 仅当可执行文件退出时才执行任务
- html - 如何按文件名过滤文件输入