我应该公开异步方法吗?

时间:2013-01-25 14:19:08

标签: vb.net async-await

我正在开发一个包含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方法?

2 个答案:

答案 0 :(得分:4)

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

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

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

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

答案 1 :(得分: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 // ...
    // ...
}

请注意,在第一次等待之前会抛出异常,但只有在检查Task时才会重新抛出异常。是等待还是获得结果。如果您选择以下列方式实现它:

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 // ...
    // ...
}

现在异常可以在其调用中捕获,您不必检查任务。在前一种情况下,即使我们还没有启动异步部分,但异常将存储在生成的Task中。