C# 异步异常包装

Mat*_*att 5 .net c# asynchronous

我一直在玩 .NET 4.6.1 中的异步调用,我想知道从期望异步方法但实际上是同步的接口的实现者抛出错误的正确方法是什么。例如:

public interface ISomeInterface
{
    Task ExecuteAsync();
}

public class SomeClass : ISomeInterface
{
    public Task ExecuteAsync()
    {
        return Task.FromException(new Exception());
    }
}
Run Code Online (Sandbox Code Playgroud)

我在Task.FromException 这里找到

所以这是 .NET 4.6 似乎仍然建议包装异常。但是,我可以编写以下代码:

public class SomeClass : ISomeInterface
{
    public Task ExecuteAsync()
    {
        throw new Exception();
    }
}
Run Code Online (Sandbox Code Playgroud)

当我使用 try/catch 块调用第二个实现时,客户端捕获了Exception,我认为这就是我们Task.FromException首先使用的原因,而且它还包含原始异常的整个调用堆栈(而方法仅对客户端的等待操作有一个堆栈跟踪)。那么似乎第二种方法更好,但似乎每个人都在使用方法一。方法一现在是否由于 实现的变化而过时了async,还是我遗漏了什么?

我还在堆栈跟踪中注意到async方法现在不会在调用之间引入任何额外的帧。我假设这只是为了简化读取堆栈跟踪?

Ste*_*ary 3

抛出错误的正确方法是来自期望异步方法但实际上是同步的接口的实现者。

正如您所发现的,您可以直接抛出异常,也可以将异常放置在返回的Task.

请注意,这确实会改变观察到异常的位置:

var task = obj1.ExecuteAsync();
await task;
Run Code Online (Sandbox Code Playgroud)

如果直接抛出异常,则在调用时抛出ExecuteAsync。如果异常发生在返回的任务上,则在await执行该任务时抛出该异常。大多数时候,任务await在调用方法后立即执行,但并非总是如此(例如,在Task.WhenAll各种场景中)。

对于异步(任务返回)API,返回的任务代表方法的执行。async- 实现的 API 总是在返回的任务上放置任何异常。所以,我想说任务返回 API 的期望是任务会收到异常。

如果出现愚蠢的例外情况,您可以采用任何一种方式来说明情况。由于异常表明代码错误,因此何时引发并不重要。例如,LINQ to Objects 总是会立即引发愚蠢的异常,而不是在枚举其返回的枚举器时引发异常。

但是,对于所有其他类型的异常,它们绝对应该继续返回Task。就我个人而言,我只是将所有异常都放在返回的Task.