捕获匿名回调中的异常

时间:2015-06-22 17:29:06

标签: c# callback try-catch

我不确定为什么这不起作用。我怀疑它与单独的工作线程有关。我还没有找到以这种方式捕获异常的任何工作。

我想我可以创建一个Task对象然后运行它,但我更愿意保留这种架构,因为其中包含的代码非常复杂。

public void MethodOne(){         
   try{
     MethodTwo(response =>{ 
        //Do something with the response
     });
   }
   catch(Exception error){ 
     //This never executes when method two throws exception
   }
}


public void MethodTwo(Action<Object> callback){
   //Conduct async call to external server 
   AppServer.MakeCall( response =>{       
      if(response.IsValid)
       callback(response.Object);
      else
        throw new FooException();
   });
}

2 个答案:

答案 0 :(得分:1)

因为这个程序是异步的,所以即使在MethodTwo返回很久之后调用回调并且该线程继续离开try块并做更大更好的事情,回调也不会被调用。在可能遥远的未来某个时刻,另一个线程正在调用回调。

正如您自己提到的,一种可能性是使用Task而不是使用回调。 TPL的一个主要优点是它如何处理错误处理。如果异步方法返回Task,您不仅可以使用ContinueWith(或await)添加回调,还可以处理这些延续中的错误。

使用回调处理此问题的方法是接受两个回调,一个在响应有效时调用,另一个在出现异常/错误时调用。调用者与使用try/catch的情况不同,但它在基于回调的模型中是最好的。

答案 1 :(得分:0)

Servy是对的。因为它已经异步使用另一个线程或任务可能不是必要的。但是你可以使用TaskCompletionSource

    public void MethodOne()
    {
        MethodTwo()
            .ContinueWith(task =>
            {
                if (task.IsFaulted)
                    // Handle error in task.Exception
                    ;
                else
                {
                    object obj = task.Result;
                    // Handle object
                }
            });
    }


    public Task<object> MethodTwo()
    {
        TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();

        //Conduct async call to external server 
        AppServer.MakeCall(response =>
        {
            if (Response.IsValid)
                tcs.TrySetResult(response.Object);
            else
                tcs.TrySetException(new FooException());
        });

        return tcs.Task;
    }

您还可以在MethodTwo lambda参数内执行try / catch和/或仅使用完整的Response对象而不是响应数据Object实例。

    public void MethodOne()
    {
        MethodTwo(response =>
            {
                if (Response.IsValid)
                    callback(Response.Object);
                else
                    throw new FooException();  // Or error handling directly
            });
    }


    public void MethodTwo(Action<Response> callback)
    {
        //Conduct async call to external server 
        AppServer.MakeCall(response =>
        {
            callback(response);
        });
    }

如果不允许从MethodOne引用Response对象,和/或不允许更改回调签名,则可以使用FooException作为参数callback(new FooException);

    public void MethodOne()
    {
        MethodTwo(response =>
            {
                if (response is FooException)
                {
                    FooException exc = response as FooException;
                }
                else
                {
                    // Handle response;
                }
            });
    }

    public void MethodTwo(Action<object> callback)
    {
        //Conduct async call to external server 
        AppServer.MakeCall(response =>
        {
            if (Response.IsValid)
                callback(Response.Object);
            else
                callback(new FooException());
        });
    }