2016-11-17 127 views
4

我一直在使用.NET 4.6.1中的异步调用,我想知道正确的方式来抛出错误是从一个接口的实现者,期望一个异步方法,但实际上是同步的。例如:C#异步异常包装

public interface ISomeInterface 
{ 
    Task ExecuteAsync(); 
} 

public class SomeClass : ISomeInterface 
{ 
    public Task ExecuteAsync() 
    { 
     return Task.FromException(new Exception()); 
    } 
} 

我发现Task.FromExceptionhere

所以这是.NET 4.6仍然看似建议包装异常。不过,我可以只写了下面的代码:

public class SomeClass : ISomeInterface 
{ 
    public Task ExecuteAsync() 
    { 
     throw new Exception(); 
    } 
} 

当我使用try/catch块称为第二个实现,客户端抓住了Exception,我认为就是为什么我们在第一时间使用Task.FromException,什么更多的是它还包含了整个调用堆栈到原始异常(而方法一只有一个堆栈跟踪到客户端的等待操作)。看来第二种方法更好,但似乎每个人都在使用方法一。由于async的实施变更,现在已经接近过时了,还是我缺少某些东西?

我也注意到在堆栈跟踪中,async方法现在不会在调用之间引入任何附加帧。我假设这只是为了简化堆栈跟踪?

回答

1

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

正如您发现的那样,您可以直接抛出异常或将异常放在返回的Task上。

注意观察异常的地方,这并不改变:

var task = obj1.ExecuteAsync(); 
await task; 

如果异常,直接抛向,它的时候ExecuteAsync被抛出时调用。如果异常放置在返回的任务上,则在任务为await ed时抛出异常。 大部分是的时候,任务是await ed之后​​立即调用该方法,但并不总是(例如在Task.WhenAll种情况下)。

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

boneheaded exceptions的情况下,您可以采用任何方式。由于异常表示代码错误,因此在引发时无关紧要。例如,LINQ to Objects将会立即引发头部异常,而不是枚举返回的枚举器时。

但是,对于所有其他类型的例外情况,他们肯定应该返回Task。就个人而言,我只是在返回的Task上放置所有例外。

+0

啊哈,明白了!谢谢你澄清斯蒂芬。 – Matt