C# 编译器如何将返回值转换为返回任务<;价值>;在异步方法中?

C# 编译器如何将返回值转换为返回任务<;价值>;在异步方法中?,c#,.net,asynchronous,.net-core,async-await,C#,.net,Asynchronous,.net Core,Async Await,我设计了以下方法来创建记录 public Task<Guid> NotAwaited() { Account account = new Account(); Context.Accounts.Add(account); Context.SaveChangesAsync(); return new Task<Guid>(() => account.Id); } 我知道,account.Id部分以某种方式转换为任务。我只是不知道怎么做。感觉就像是黑魔

我设计了以下方法来创建记录

public Task<Guid> NotAwaited()
{
  Account account = new Account();
  Context.Accounts.Add(account);
  Context.SaveChangesAsync();
  return new Task<Guid>(() => account.Id);
}
我知道,
account.Id
部分以某种方式转换为任务。我只是不知道怎么做。感觉就像是黑魔法(我知道不是)

是否存在隐式转换?还是我仍然不正确地执行异步调用

感觉就像是黑魔法

可能是的

您编写C#代码,然后编译器将其拆分为多个部分一起运行,并创建一个状态机,每当等待的异步任务完成时,该状态机都会“向前移动”。如果调试代码,调试器知道如何在调试器中表示“局部变量”(实际上可能是状态机类型上的实例成员),并映射到原始源代码的行

因此,对于您的情况,代码可能类似于(通过sharplab创建,请参阅):

[AsyncStateMachine(typeof(d__0))]
等待的公共任务()
{
d_u0状态机=默认值(d_u0);
stateMachine.t__builder=AsyncTaskMethodBuilder.Create();
stateMachine.1_uu状态=-1;
AsyncTaskMethodBuilder t\uuuu builder=stateMachine.t\uu builder;
启动(参考状态机);
返回stateMachine.t\u builder.Task;
}
[StructLayout(LayoutKind.Auto)]
[编译生成]
私有结构d__0:IAsyncStateMachine
{
州内公共交通;
公共异步TaskMethodBuilder t_uuBuilder;
私人帐户5__2;
私人任务等待者1;
私有void MoveNext()
{
int num=1___状态;
Guid id;
尝试
{
任务等待者;
如果(num!=0)
{
5__2=新账户();
Context.Accounts.Add(5__2);
waiter=Context.SaveChangesAsync().getwaiter();
如果(!waiter.IsCompleted)
{
num=(1__状态=0);
u___1=等待者;
建造商等待未安全完成(参考等待者,参考此);
返回;
}
}
其他的
{
等待者=u___1;
u__1=默认值(任务等待器);
num=(1__状态=-1);
}
GetResult();
id=5_uuu2.id;
}
捕获(异常)
{
1_uu状态=-2;
设置异常(异常);
返回;
}
1_uu状态=-2;
设置结果(id);
}
void IAsyncStateMachine.MoveNext()
{
//ILSpy根据MoveNext中的.override指令生成此显式接口实现
this.MoveNext();
}
[调试程序隐藏]
私有void SetStateMachine(IAsyncStateMachine stateMachine)
{
t_ubuilder.SetStateMachine(stateMachine);
}
void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine状态机)
{
//ILSpy根据SetStateMachine中的.override指令生成此显式接口实现
此.SetStateMachine(stateMachine);
}
}
您可以看到,调用
savechangessync()
的一方位于不同于访问Id属性的逻辑分支中。局部变量
account
现在是生成的结构上的
5_uu2
字段。所使用的标识符名称实际上都不是有效的C#标识符,但在编译到的底层IL语言中有效,上述代码是实际生成的代码的反编译C#ish表示

调用
Awaited()
方法将实际创建“隐藏的”
d_u0
结构的新实例(在调试模式下,它将是一个
类,而不是一个
struct
,以支持编辑和继续),并使用异步基础结构的类型连接此状态机并运行它

MoveNext()
在启动状态机时调用,但在每次等待的任务完成时也会调用(作为延续)。您可以看到,最后一部分将结果设置为
id
值,这基本上是您的
return
语句


值得一提的是,在大多数代码中都有一个try-catch,它将异常包装到任务结果中,因此您可以
在代码(或从代码调用的代码)中抛出
,并在调度方法的异步部分时创建失败的任务,而不是未处理的异常

您可以将
async
视为将结果(返回值和异常)包装到
任务中

同样,
wait
打开结果(提取返回值或引发异常)


我有一个更详细的计划,我建议跟进。另一方面,.

很高兴将返回的guid自动包装到任务中。简短回答:编译器执行了一些黑魔法,将返回类型转换为
任务。很长的一个答案涉及到理解编译器是如何生成状态机的,这些状态机必须使用
wait
@chieftwoils来创建好的,这让我更加放心。但是,由于它会自动将内容包装到任务中,我如何/应该控制成功状态?在实际代码中,有一个场景,我希望任务失败,并且在API中的消费控制器中生成
Ok(guid)
NotFound(guid)
…@camiloterevento谢谢,伙计。目前我对这个简短的版本很满意。然而,由于这是自动行为,我想知道如何控制任务的成功状态。请看我上面的评论,了解实际的用例描述。这是好的部分,你不在乎。您知道,在调用
var result=await-Awaited()之后您将有一个Guid(或一个异常,如果是这样的话)
public async Task<Guid> Awaited()
{
  Account account = new Account();
  Context.Accounts.Add(account);
  await Context.SaveChangesAsync();
  return account.Id;
}
[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);
    }
}