1

我正在开发一个包含 RESTful Web 服务的类库(供其他开发人员使用)。我正在使用命名空间中的HttpClient类型来System.Net.Http异步执行我的所有 API 调用。

除了简单地发送/接收内容之外,我还想对返回的 XML 数据进行额外的处理。不管你怎么说,这涉及到在类库的某个地方使用 Async/Await 关键字。

我的问题是公开使用 Async 关键字的方法是好主意还是坏主意,以及为什么。我在某处读到您应该将 Async 方法设为私有,然后仅使用 return 语句创建一个附加方法。这就是我一直在做的,但感觉不对。

Public Function InvokeAsync(command As HttpRequestMessage) As Task(Of CommandResult)
    Return InvokeAsyncInternal(command)
End Function

Private Async Function InvokeAsyncInternal(command As HttpRequestMessage) As Task(Of CommandResult)
    Dim rawCommandResult As HttpResponseMessage = Await myHttpClient.SendAsync(command)
    Dim finalResult As CommandResult = AdditionalProcessing(rawCommandResult)
    Return finalResult
End Function

请记住,这是一个过于简化的代码示例:是否有任何好的论据不直接公开 Async 方法?

4

2 回答 2

4

这与编译器转换有关;您可以将“真正的”Async方法与其对应的方法分开,并且您将获得与对迭代器( )方法Public进行相同类型的分离时相同的好处。Yield

特别是,从包装器抛出的异常与从Async方法抛出的异常处理方式不同。当Async返回的方法Task抛出一个Exception时,它被放置在返回Task而不是直接抛出给调用者。当Public包装方法(不是Async)抛出一个Exception时,它会直接抛出给调用者。

因此,可以在Public方法中放入前置条件式检查。调用者可以忽略Tasks 上的异常,但不能忽略直接抛出的异常。将前置条件异常放在Public包装器中会强制调用者意识到他们正在滥用 API,并且还允许将使用异常(直接抛出)与运行时异常(放置在Task.

如果您的方法没有先决条件,那么您的Public包装器只是返回内部Task. 在这种情况下,Public包装器是不必要的。

于 2013-01-25T14:39:10.887 回答
0

考虑以下场景:

void Caller()
{
    var t = AsyncThatThrows(null);
    // ...
    Task.WaitAny(t, t2); // <-- exception thrown here
}

Task AsyncThatThrows(Object o)
{
    if (o == null)
        throw new ArgumentNullException("o");

    // ...
    await // ...
    // ...
}

请注意,异常是在第一次等待之前引发的,但仅在检查任务时才会重新引发异常。无论是通过等待它还是通过获取它的结果。如果您选择通过以下方式实现它:

void Caller()
{
    var t = AsyncThatThrows(null);// <-- exception thrown here
    // ...
    Task.WaitAny(t, t2);
}

Task AsyncThatThrows(Object o)
{
    if (o == null)
        throw new ArgumentNullException("o");

    return AsyncThatThrows_Impl(o);
}

Task AsyncThatThrows_Impl(Object o)
{
    // ...
    await // ...
    // ...
}

现在可以在调用时捕获异常,您不必检查任务。在前一种情况下,即使我们还没有开始异步部分,异常仍然会存储在结果任务中。

于 2013-01-25T17:10:52.107 回答