Как создать универсальный метод расширения для асинхронных методов?

#c# #async-await #task #extension-methods

#c# #async-await #задача #расширение-методы

Вопрос:

Я пытаюсь создать .WithDelay(seconds); метод, который я могу добавить в конце вызовов асинхронных методов.

Проблема, с которой я сталкиваюсь, заключается в том, что сначала вызывается асинхронный метод, затем происходит задержка, я хочу, чтобы все было наоборот, без переключения порядка вызова.

Например, я хочу, await MyMethod().WithDelay(seconds); а не await WithDelay(seconds).MyMethod(); .

Вот что у меня есть на данный момент, сначала он вызывает метод:

 public async static Task<T> WithDelay<T>(this Task<T> task, int delay) {
  await Task.Delay(delay);
  return await task;
}
  

Я ожидаю, что сначала произойдет задержка, а затем будет запущен сам метод.

Ответ №1:

Я хочу, чтобы было наоборот, без переключения порядка вызова.

Это невозможно, потому что язык C # поддерживает методы расширения только для типов, а не для методов.

Ближайший, который вы можете получить, — это метод расширения для делегатов:

 public static async Task<T> WithDelay<T>(this Func<Task<T>> func, int delay) {
  await Task.Delay(delay);
  return await func();
}
  

Использование по-прежнему оказывается неудобным:

 // Either:
Func<Task<MyType>> func = MyMethod;
var result = await func.WithDelay(1000);

// or (assuming "using static"):
var result = await WithDelay(MyMethod, 1000);

// What you really want, not currently possible:
// var result = await MyMethod.WithDelay(1000);
  

В подобных ситуациях, связанных с типом, это может помочь сначала решить проблему синхронно, а затем преобразовать это решение в async . Если язык препятствует хорошему синхронному решению, то он, скорее всего, будет препятствовать хорошему асинхронному решению.

Есть предложение по методам расширения в methods, но сегодня это не является частью языка.

Комментарии:

1. Не тот ответ, который я искал, но спасибо. Я прекращу поиски.

Ответ №2:

Сработает ли приведенное ниже?

 public static Task Async<T>(this T o, Action<T> action, CancellationToken token = default) {
  if (token.IsCancellationRequested) {
    return Task.FromCanceled(token);
  }
  try {
    action(o);
    return Task.CompletedTask;
  } catch (Exception e) {
  return Task.FromException(e);
  }
}
  

Пример:

 public static Task CommitAsync(this IDbTransaction transaction, CancellationToken token = default) 
  => transaction.Async(x=> transaction.Commit(), token);