【问题标题】:Returning objects from GetSomethingAsync callbacks从 GetSomethingAsync 回调中返回对象
【发布时间】:2013-07-03 00:04:05
【问题描述】:

我正在使用DropNet API 连接到 DropBox。不过,我在 async/await 概念上苦苦挣扎。

我有一个方法是调用 apiGetTokenAsync。返回类型为void,回调有成功和失败动作。

public async Task<GetTokenResult> GetAuthorizationUrl()
{
    var result = new GetTokenResult();

    _dropNetClient.GetTokenAsync(
        login =>
            {
                result.Url = _dropNetClient.BuildAuthorizeUrl(_authorizationCallback.ToString());
                result.Success = true;
            },
        exception =>
            {
                result.Error = exception.ToDiagnosticString();
                result.Success = false;
            }
        );

    return result;
}

问题?我在想将返回类型更改为 GetTokenResult 可能 比操作返回的速度更快,因此我的结果永远不会设置。我不能 await async 方法,因为它返回 void。

这是我无法理解的关于 async/await 的一个概念。

【问题讨论】:

    标签: c# windows-phone-7 task async-await


    【解决方案1】:

    您可能需要考虑使用TaskCompletionSource

    public Task<GetTokenResult> GetAuthorizationUrl()
    {
        var tcs = new TaskCompletionSource<GetTokenResult>();   
        _dropNetClient.GetTokenAsync(
            login => tcs.SetResult(new GetTokenResult { 
                Url = _dropNetClient.BuildAuthorizeUrl(
                          _authorizationCallback.ToString()),
                Success = true
            },
            exception => tcs.SetResult(new GetTokenResult { 
                Error = exception.ToDiagnosticString(),
                Success = true
            });
        return tcs.Task;
    }
    

    只有在GetTokenAsync 操作完成(通过其中一个回调)时,返回的任务才会完成,您可以通过异步方法等待它。

    我个人会在失败时使用SetException 而不是SetResult - 这样,如果您等待返回的任务,它会在异常上抛出适当的失败,而不仅仅是设置不同的值。它更像是 .NET 的惯用语。

    编辑:然后您可以更改代码以返回 Task&lt;string&gt;

    public Task<string> GetAuthorizationUrl()
    {
        var tcs = new TaskCompletionSource<string>();   
        _dropNetClient.GetTokenAsync(
            login => tcs.SetResult(_dropNetClient.BuildAuthorizeUrl
                                        _authorizationCallback.ToString()),
            exception => tcs.SetException(exception));
        return tcs.Task;
    }
    

    现在异常将在任务本身内传播 - 如果您 await 一个任务出错,则此时将引发异常。不需要额外的属性等 - 它更接近于您编写相应同步代码的方式。

    【讨论】:

    • 谢谢乔恩。你能举个例子来说明这个方法和 SetException 的用法吗?我无法理解并查看您将在哪里消费异常。在此方法的调用者中,我假设您要等待 GetAuthorizationUrl(),它会删除 GetTokenResult 而不再是任务。
    • @TimGabrhel:“删除 GetTokenResult 而不再是任务”是什么意思?是的,您会等待GetAuthorizationUrl - 但是如果您使用tcs.SetException(exception) 而不是让您的 GetTokenResult 具有错误/成功属性,这意味着您可以根据需要捕获异常,或者让它传播。哎呀,您甚至可以更改为仅使用 Task&lt;string&gt; 作为您返回的唯一 成功 信息是一个字符串。
    • 我对异常的使用很感兴趣。你是说你必须在这个方法中消耗异常吗?就我而言,我想将异常传播回 UI (WindowsPhone),以便允许用户通过电子邮件发送包含异常详细信息的错误报告。因为我想传播,所以如果由于 await 关键字,调用者的实际返回类型是 GetTokenResult,我将如何返回异常详细信息的概念。
    • @TimGabrhel:不,关键是回调会在TaskCompletionSource 中设置异常,然后当您等待任务时,此时会抛出异常。这比在属性中设置它更惯用。我并不是建议将返回类型设为GetTokenResult - 看看我给你的方法......它是Task&lt;GetTokenResult&gt;。当你等待的时候,你会得到一个GetTokenResult。但我是说你可以把它改成Task&lt;string&gt;,只是在成功的情况下传播URL,在失败的情况下设置一个异常。将更新答案。
    • 非常感谢。我错过了这一点:如果您等待出现故障的任务,则会在该点引发异常。.
    猜你喜欢
    • 2016-03-17
    • 2020-08-14
    • 2015-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-17
    • 2012-02-02
    • 2020-04-24
    相关资源
    最近更新 更多