Пока еще в .NET существуют классы (особенно от сторонних разработчиков), которые используют старую модель для асинхронности. Речь идет реализации с помощью событий. Однако, преобразовать такой код к виду async/await очень легко.
Поможет в этом TaskCompletionSource<T>. Для примера возьмем некий класс Controller. Он содержит метод BeginRead() для начала операции асинхронного чтения данных. После её завершения пользователь может получить уведомление с помощью события Completed.
Стандартный для подобного подхода код выглядит примерно так:
var controller = new Controller();
controller.Completed += (s, e) => { /* обработка прочитанных данных */ };
controller.BeginRead();
Его можно переписать в стиле async/await c помощью своего метода-расширения:
public static async Task ReadAsync(this Controller controller)
{
var tcs = new TaskCompletionSource<object>();
EventHandler handler = (s, e) => tcs.TrySetResult(null);
try {
controller.Completed += handler;
controller.BeginRead();
await tcs.Task;
}
finally {
controller.Completed -= handler;
}
}
И теперь в коде приложения можно использовать более понятную запись:
await controller.ReadAsync();
/* обработка прочитанных данных */
Кстати, подобные методы-расширения можно создавать и для простого ожидания событий. Например:
public static async Task WaitForClick(this Button button)
{
var tcs = new TaskCompletionSource<object>();
EventHandler handler = (s, e) => tcs.TrySetResult(null);
try {
button.Click += handler;
await tcs.Task;
}
finally {
button.Click -= handler;
}
}
Это позволит ожидать нажатия кнопки следующим вызовом:
await button.WaitForClick();