2013-01-25 114 views
1

我正在开发一个包装REST风格的Web服务的类库(供其他开发人员使用)。我使用System.Net.Http名称空间中的HttpClient类型异步执行所有API调用。我应该公开异步方法吗?

除了简单地发送/接收内容,我还想对返回的XML数据进行额外的处理。无论你如何放置它,都需要在类库中的某处使用Async/Await关键字。

我的问题是,揭示使用Async关键字的方法以及为什么是好的或坏主意。我读过某处应该让Async方法为private,然后用一个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方法?

+0

“我读过的地方......”你在哪里读到的?你有链接吗?总的来说,我认为你不应该因为有人这样说而做某件事。你应该这样做,因为他们的论点是有道理的,你同意它。 – svick

+0

这是在互联网上的许多“异步/等待”博客文章之一。这在当时是有道理的,但现在我开始重新考虑,因为它似乎没有增加任何价值。 –

回答

3

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

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

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

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

+0

因此,只包装我的方法,以避免暴露与“Async”或“Iterator”关键字的方法是完全没有意义的?错误的输入由Web服务正确处理,所以我不必担心验证。 –

+0

这并非毫无意义,有时它是必要的。例如,当您在应用程序的第一部分抛出异常时,除非您等待该功能,否则不会自然获得该异常。 –

+0

@ToniPetrina但包装的方法并没有做任何事情。它不会影响如何表示异常。 – svick

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

注意,异常之前抛出第一的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 // ... 
    // ... 
} 

现在异常可以在其调用被抓,你没有检查的任务。在前一种情况下,尽管我们还没有开始异步部件,但异常仍将存储在生成的任务中。

+0

对于不知道编译器做什么的人来说,这个东西是非常难以理解的 –

相关问题