实现返回Task的方法时的合同协议
在实现返回关于抛出exception的Task的方法时,是否存在MS“最佳实践”或合同协议? 这是在编写unit testing时出现的,我试图弄清楚我是否应该测试/处理这种情况(我认识到答案可能是“防御性编码”,但我不希望这是答案)。
即
-
方法必须始终返回一个Task,该Task应包含抛出的Exception。
-
方法必须始终返回一个Task,除非该方法提供无效参数(即ArgumentException)。
-
方法必须总是返回一个任务,除非开发人员变得流氓并做他/她想要的事情(jk)。
Task Foo1Async(string id){ if(id == null){ throw new ArgumentNullException(); } // do stuff } Task Foo2Async(string id){ if(id == null){ var source = new TaskCompletionSource(); source.SetException(new ArgumentNullException()); return source.Task; } // do stuff } Task Bar(string id){ // argument checking if(id == null) throw new ArgumentNullException("id") try{ return this.SomeService.GetAsync(id).ContinueWith(t => { // checking for Fault state here // pass exception through. }) }catch(Exception ex){ // handling more Fault state here. // defensive code. // return Task with Exception. var source = new TaskCompletionSource(); source.SetException(ex); return source.Task; } }
我最近问过一个类似的问题:
处理异步方法的同步部分中的exception 。
如果该方法具有async
签名,则抛出方法的同步或异步部分无关紧要。 在这两种情况下,exception都将存储在Task
。 唯一的区别是,在前一种情况下,生成的Task
对象将立即完成(出现故障)。
如果该方法没有async
签名,则可能会在调用者的堆栈帧上抛出exception。
IMO,在任何一种情况下,调用者都不应该对是否从同步或异步部分抛出exception,或者该方法是否具有async
签名做出任何假设 。
如果您确实需要知道任务是否已同步完成,则可以始终检查其Task.Completed
/ Task.Completed
/ Cancelled
状态或Task.Exception
属性,而无需等待:
try { var task = Foo1Async(id); // check if completed synchronously with any error // other than OperationCanceledException if (task.IsFaulted) { // you have three options here: // 1) Inspect task.Exception // 2) re-throw with await, if the caller is an async method await task; // 3) re-throw by checking task.Result // or calling task.Wait(), the latter works for both Task and Task } } catch (Exception e) { // handle exceptions from synchronous part of Foo1Async, // if it doesn't have `async` signature Debug.Print(e.ToString()) throw; }
但是, 通常你应该await
result
,而不关心任务是否同步或异步完成,以及哪个部分可能抛出。 将在调用者上下文中重新抛出任何exception:
try { var result = await Foo1Async(id); } catch (Exception ex) { // handle it Debug.Print(ex.ToString()); }
这也适用于unit testing,只要async
方法返回一个Task
(unit testing引擎不支持async void
方法,AFAIK,这是有意义的:没有Task
可以跟踪和await
)。
回到你的代码,我会这样说:
Task Foo1Async(string id){ if(id == null) { throw new ArgumentNullException(); } // do stuff } Task Foo2Async(string id) { if(id == null){ throw new ArgumentNullException(); } // do stuff } Task Bar(string id) { // argument checking if(id == null) throw new ArgumentNullException("id") return this.SomeService.GetAsync(id); }
让Foo1Async
, Foo2Async
, Bar
的调用者处理exception,而不是手动捕获和传播它们。
我知道Jon Skeet喜欢在单独的同步方法中进行前置条件检查,以便直接抛出它们。
但是,我自己的观点是“无所谓”。 考虑Eric Lippert的exception分类 。 我们都同意外部exception应放在返回的Task
(不直接抛出调用者的堆栈帧)。 应完全避免烦恼的例外情况。 所讨论的唯一类型的例外是愚蠢的例外(例如,参数例外)。
我的论点是,抛出它们并不重要,因为你不应该编写能够捕获它们的生产代码 。 你的unit testing是唯一应该捕获ArgumentException
和朋友的代码,如果你使用await
那么它们何时被抛出并不重要。
方法返回任务的一般情况是因为它们是异步方法。 在这些情况下,通常应该抛出方法的同步部分中的exception,就像在任何其他方法中一样,并且在异步部分中应该存储在返回的Task
内部(通过调用async
方法或匿名委托自动)。
因此,在像无效参数这样的简单情况下,只需像Foo1Async
一样抛出exception。 在有关异步操作的更复杂的情况下,在Foo2Async
为返回的任务设置了一个exception
这个答案假设您指的是未标记为async
Task
返回方法。 在那些你无法控制正在创建的任务的情况下,任何exception都会自动存储在该任务中(因此问题将无关紧要)。
上述就是C#学习教程:实现返回Task的方法时的合同协议分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/1019112.html