实施返回任务的方法时合同协议书 [英] Contract agreement when implementing a method that returns a Task

查看:206
本文介绍了实施返回任务的方法时合同协议书的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有没有MS最佳实践或合同约定执行,返回任务的问候抛出异常的方法时?这提出了编写单元测试的时候,我试图找出如果我要测试/处理这种情况(我承认,答案可能是防御性的编码,但我不希望这样的答案)。

  1. 方法必须返回一个任务,它应该包含抛出异常。

  2. 方法必须返回一个任务,除非该方法提供了无效的参数(如ArgumentException的)。

  3. 方法必须返回一个任务,除了当开发商去流氓和做他/她想要的东西(JK)。

 任务Foo1Async(字符串ID){
  如果(ID == NULL){
    抛出新ArgumentNullException();
   }

  //做的东西
}

任务Foo2Async(字符串ID){
  如果(ID == NULL){
    无功源=新TaskCompletionSource<布尔>();
    source.SetException(新ArgumentNullException());
    返回source.Task;
  }

  //做的东西
}

任务栏(字符串ID){
  //参数检查
  如果(ID == NULL)抛出新ArgumentNullException(ID)

  尝试{
    返回this.SomeService.GetAsync(ID).ContinueWith(T => {
       //检查故障状态在这里
       //传递例外通过。
    })
  }赶上(例外前){
    //这里处理更多的故障状态。
    //防守code。
    //返回任务有例外。
    无功源=新TaskCompletionSource<布尔>();
    source.SetException(前);
    返回source.Task;
  }
}
 

解决方案

我问一个有些类似的问题最近:

<一个href="http://stackoverflow.com/questions/21055920/handling-exceptions-from-the-synchronous-part-of-async-method">Handling例外情况的异步方法同步的部分。

如果该方法有一个异步签名,这并不重要,如果你从方法的同步或异步的一部分抛出关系。在这两种情况下,异常将被存储在工作里面。唯一的区别是,所得到的任务对象将立即在前者的情况下完成的(故障)。

如果该方法没有异步签名时,可能会引发异常调用程序的堆栈帧。

IMO,在两种情况下,主叫方的不应该做出的异常是否已被抛出的同步或异步的一部分的任何假设或方法是否具有异步签名,在所有。

如果你真的需要知道,如果任务已经完成同步,可以随时查询其 Task.Completed / 断陷 / 取消状态,或 Task.Exception 属性,而不等待:

 尝试
{
    变种任务= Foo1Async(ID);
    //检查是否同步完成任何错误
    //比OperationCanceledException其他
    如果(task.IsFaulted)
    {
        //你有三个选择在这里:

        // 1)检查task.Exception

        // 2)再次引发与计谋,如果主叫方是异步方法
        等待任务;

        // 3)重新掷通过检查task.Result
        //或调用task.Wait(),后者既用于工作&LT; T&GT;和任务
    }
}
赶上(例外五)
{
    //从Foo1Async同步部分处理异常,
    如果它不具有//`async`签名
    Debug.Print(e.ToString())
    扔;
}
 

不过,通常你应该只等待结果 ,不若照顾任务完成同步或异步,以及哪部分可能抛出。任何异常将被重新抛出在调用程序上下文:

 尝试
{
    VAR的结果=等待Foo1Async(ID);
}
赶上(例外前)
{
    // 处理它
    Debug.Print(ex.ToString());
}
 

这适用于单元测试过,只要异步方法返回一个工作(单元测试引擎不支持异步无效方法,AFAIK,这是有道理的:没有工作来保持跟踪和等待)。

返回到code,我会这样说:

 任务Foo1Async(字符串ID){
  如果(ID == NULL){
    抛出新ArgumentNullException();
   }

  //做的东西
}

任务Foo2Async(字符串ID){
  如果(ID == NULL){
    抛出新ArgumentNullException();
   }

  //做的东西
}

任务栏(字符串ID){
  //参数检查
  如果(ID == NULL)抛出新ArgumentNullException(ID)
  返回this.SomeService.GetAsync(ID);
}
 

Foo1Async Foo2Async 酒吧处理的异常,而不是捕获和人工繁殖它们。

Is there a MS "best practice" or contract agreement when implementing a method that returns a Task in regards to throwing exceptions? This came up when writing unit tests and I was trying to figure out if I should to test/handle this condition (I recognize that the answer could be "defensive coding", but I don't want that to be the answer).

i.e.

  1. Method must always return a Task, which should contain the thrown Exception.

  2. Method must always return a Task, except when the method supplies invalid arguments (i.e. ArgumentException).

  3. Method must always return a Task, except when the developer goes rogue and does what he/she wants (jk).

Task Foo1Async(string id){
  if(id == null){
    throw new ArgumentNullException();
   }

  // do stuff
}

Task Foo2Async(string id){
  if(id == null){
    var source = new TaskCompletionSource<bool>();
    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<bool>();
    source.SetException(ex);
    return source.Task;
  }
}

解决方案

I've asked a somewhat similar question recently:

Handling exceptions from the synchronous part of async method.

If the method has an async signature, it doesn't matter if you throw from the synchronous or asynchronous part of method. In both cases, the exception will be stored inside the Task. The only difference is that the resulting Task object will be instantly completed (faulted) in the former case.

If the method doesn't have async signature, the exception may be thrown on the caller's stack frame.

IMO, in either case the caller should not make any assumption about whether the exception has been thrown from the synchronous or asynchronous part, or whether the method has async signature, at all.

If you really need to know if the task has completed synchronously, you can always check its Task.Completed/Faulted/Cancelled status, or Task.Exception property, without awaiting:

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<T> and Task 
    }
}
catch (Exception e)
{
    // handle exceptions from synchronous part of Foo1Async,
    // if it doesn't have `async` signature 
    Debug.Print(e.ToString())
    throw;
}

However, normally you should just await the result, without caring if the task has completed synchronously or asynchronously, and which part has possibly thrown. Any exception will be re-thrown on the caller context:

try
{
    var result = await Foo1Async(id); 
}
catch (Exception ex)
{
    // handle it
    Debug.Print(ex.ToString());
}

This works for unit testing too, as long as the async method returns a Task (the Unit Test engine doesn't support async void method, AFAIK, which makes sense: there is no Task to keep track of and await).

Back to your code, I'd put it this way:

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);
}

Let the caller of Foo1Async, Foo2Async, Bar deal with the exceptions, rather than capturing and propagating them manually.

这篇关于实施返回任务的方法时合同协议书的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆