2014-11-28 3 views
3

Я пытаюсь выяснить, что я делаю неправильно здесь. Я уверен, что это что-то глупое, но я мог бы использовать дополнительные глаза, чтобы понять, что это такое. Я пытаюсь создать рабочий сервис, которому вы можете назначить делегата рабочего действия. Когда я пытаюсь поймать исключение, созданное делегатом рабочего действия, обработчик не вызывается. Я думаю, это потому, что мое действие на самом деле является асинхронным методом, который возвращает Task, и его не ждут. Но как я должен знать, что делегат асинхронен? Почему C# позволяет мне назначать метод, который возвращает задачу в переменную действия, которая должна возвращать void?Обработка исключений с делегатом async

Любая помощь будет оценена!

[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(); 
} 
+0

Итак, один из вариантов - установить WorkAction = item => Throws(). Wait(), но я до сих пор не понимаю, почему мне разрешено назначать переменную типа Action с переменной типа Func . –

+0

Я думаю, что ответ Стивена касается этого. Но повторить: на языке есть специальные правила, позволяющие это делать. Это будет подразумевать, что 'async Task' будет эквивалентно' async void', что и происходит в вашем случае, позволяя использовать выражение лямбда для типа делегата 'Action '. Но обратите внимание, что это преобразование не изменяет поведение метода async; он по-прежнему возвращается в первом выражении 'await', и поэтому, если не ожидаемый (который вы не можете, если это' void'), вызывающий может (и обычно делает) завершить до того, как делает метод 'async'. –

ответ

1

Почему C# позволяет мне назначить метод, который возвращает задачу в переменную действия, которая должна возвращать void?

async void Поскольку является законным случаем использования для асинхронных обработчиков событий, поэтому компилятор допускает пустоту возвращающейся Action<T> и не жалуется. Как только вы узнаете об этом факте, вы можете явно использовать Func<Task>, который создаст желаемую перезагрузку метода возврата Task.

Как вы говорите вы предпочитаете держать работу действие «простым» и не требуют задач, рассмотреть вопрос о предоставлении перегрузки асинхронной, которая будет вести себя правильно с потоком управления асинхронным:

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

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

Примечания это может быть в совершенно другом классе (отдельно от асинхронного рабочего с синхронизацией), согласно вашему выбору.

+1

Это то, что я закончил делать. –

4

Лямбда без возвращаемых значений может быть приведен к способу задачи возвращающих (например, Func<Task>) или методом пустот возвращения (например, Action). Обратите внимание, что при литье в метод возврата void фактический метод для этой лямбда является методом async void со всеми проблемами, которые возникают вместе с методами async void. Я описываю некоторые проблемы async void в моих best practices article; одним из них является то, что вы не можете перехватывать исключения с помощью try.

Лучшим вариантом является изменение ваших рабочих элементов, чтобы вернуть Task, что является гораздо более естественным представлением асинхронных методов.

Смежные вопросы