如何隐藏任务的任务?

How do I hide Task of Task?

考虑以下方法:

private async Task<Task<Response>> SendAsync(string data)
{
    this.Tcs = new TaskCompletionSource<Response>();

    await this.Stream.WriteAsync(...);
    await this.Stream.FlushAsync();

    return this.Tcs.Task;
}

我有一个异步方法,我希望它 return Task<Response>。但是因为我想 return TaskCompletionSource<Response> (在别处设置,所以我不能在这里等待),我实际上必须 return Task<Task<Response>> 代替。

在调用代码中,我有两种处理方法,同时从 class 外部隐藏这种丑陋之处。假设响应不重要并且可以忽略,我可以简单地 return a Task:

public Task GetAsync(string key)
{
    return this.SendAsync("GET " + key);
}

另一方面,如果我想要响应,我必须使用这个丑陋的 await await 来让它工作:

public async Task<Response> GetAsync(string key)
{
    return await await this.SendAsync("GET " + key);
}

有没有更好的方法来处理这个问题,即 return 从 SendAsync() 中获取 Task<Response>,而不在 class 之外暴露 Task<Task<Response>>,同时不使用 await await?

我不确定您为什么需要在 TaskCompletionSource 异步方法中使用。通常你要么做一个,要么做另一个。

但是如果你必须忘记 returning TaskCompletionSource.Task。只需像执行其余异步方法(WriteAsyncFlushAsync)一样等待任务并将方法更改为 return Task<Response>:

private async Task<Response> SendAsync(string data)
{
    this.Tcs = new TaskCompletionSource<Response>();

    await this.Stream.WriteAsync(...);
    await this.Stream.FlushAsync();

    return await this.Tcs.Task;
}

这样,异步方法 return 是一个在有 Response 时完成的任务,因此您只需要 await SendAsync("...") 一次。

@i3arnon 的回答是一个很好的解决方案,但是另一种解决方案是使用 Unwrap 扩展方法。

TaskExtensions.Unwrap 方法专为将 Task<Task<TResult>> 转换为 Task<TResult> 而设计,可按如下方式使用:

public Task<Response> GetAsync(string key)
{
    return this.SendAsync("GET " + key).Unwrap();
}

任何结果、异常或取消都将正确传播到结果 Task<TResult>