Exception handling with async delegate

I am trying to figure out what I am doing wrong here. I'm sure this is something silly, but I could use extra eyes to figure out what it is. I am trying to create a worker service that you can assign a worker action delegate to. When I try to catch the exception thrown by the worker activity delegate, the handler is not called. I think this is because my action is actually an asynchronous method that returns a Task, and it is not expected. But how am I supposed to know that the delegate is asynchronous? Why does C # allow me to assign a method that returns a task to an action variable that should return void?

Any help would be appreciated!

[Fact]
public async Task CanCatchExceptions() {
    var worker = new Worker {
        WorkAction = async item => await Throws()
    };
    worker.DoWork(new WorkItem { Id = 1 });
}

public class Worker {
    // would prefer to keep work action simple and not require a Task.
    public Action<WorkItem> WorkAction { get; set; }

    public void DoWork(WorkItem item) {
        try {
            WorkAction(item);
        } catch {
            Debug.WriteLine("Handled");
        }
    }
}

public class WorkItem {
    public int Id { get; set; }
}

public async Task Throws() {
    throw new ApplicationException();
}

      

+3


source to share


2 answers


Why does C # allow me to assign a method that returns a task to an action variable that should return void?

Since it async void

is legal use case for async event handlers, why does the compiler allow void to return Action<T>

and doesn't complain. Once you know this fact, you can explicitly use Func<Task>

that will create the desired method return method Task

.

As you say, you prefer the work activity to be "simple" and does not require a task to complete, consider supplying an asynchronous overload that will behave correctly with an asynchronous control flow:



public Func<WorkItem, Task> WorkAsync { get; set; }

public async Task WorkAsync(WorkItem item)
{
      try
      {
            await WorkAsync(item)
      }
      catch (Exception e)
      {
           // Handle
      }
}

      

Note that this could be in a completely different class (separate from the async worker with sync) of your choice.

+1


source


Lambdas with no return values ​​can be passed to a task return method (for example Func<Task>

) or a void return method (for example Action

). Note that when passing void to a return method, the actual method of this lambda is a method async void

with all the problems that come with methods async void

. I describe some of the issues async void

in my best practices article ; one of them is that you cannot catch exceptions with try

.



The best option is to modify your work items to return Task

, which is a much more natural representation of asynchronous methods.

+4


source







All Articles