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

133

Немного справочной информации.

Я изучаю стек веб-API и пытаюсь инкапсулировать все данные в виде объекта «Результат» с такими параметрами, как Success и ErrorCodes.

Однако разные методы дадут разные результаты и коды ошибок, но объект результата обычно создается одинаково.

Чтобы сэкономить время, а также узнать больше о возможностях async / await в C #, я пытаюсь обернуть все тела методов своих действий веб-api в делегат асинхронного действия, но попал в небольшую загвоздку ...

Учитывая следующие классы:

public class Result
{
    public bool Success { get; set; }
    public List<int> ErrorCodes{ get; set; }
}

public async Task<Result> GetResultAsync()
{
    return await DoSomethingAsync<Result>(result =>
    {
        // Do something here
        result.Success = true;

        if (SomethingIsTrue)
        {
            result.ErrorCodes.Add(404);
            result.Success = false;
        }
    }
}

Я хочу написать метод, который выполняет действие над объектом Result и возвращает его. Обычно с помощью синхронных методов это было бы

public T DoSomethingAsync<T>(Action<T> resultBody) where T : Result, new()
{
    T result = new T();
    resultBody(result);
    return result;
}

Но как мне преобразовать этот метод в асинхронный с помощью async / await?

Вот что я пробовал:

public async Task<T> DoSomethingAsync<T>(Action<T, Task> resultBody) 
    where T: Result, new()
{
    // But I don't know what do do from here.
    // What do I await?
}
Альбин Анке
источник
1
Если вы newподнимаетесь T, почему ваш метод должен быть асинхронным? AFAIK в коде с использованием асинхронных API-интерфейсов, вам нужно только распространить информацию asyncиз других методов, которые вы используете.
millimoose
Извините, я все еще новичок в этом, что вы имеете в виду, когда говорите, что вам нужно только распространять, и какое отношение к этому имеет new-ing T?
Альбин Анке
Думаю, я понял это, спасибо, миллимус, что ты дал мне о чем подумать.
Альбин Анке
1
Почему вы даже пытаетесь сделать это асинхронно? Чаще всего в ситуациях, не связанных с веб-сервером, выполнение фальшивой асинхронности путем обертывания синхронного кода в задачах (например, вы пытаетесь это сделать) медленнее, чем просто выполнение этого синхронно.
Скотт Чемберлен
1
@AlbinAnke Под «распространением» я подразумеваю, что если вы вызываете метод .NET, как Stream.ReadAsync()в методе, этот метод сам должен быть асинхронным и возвращать Task<T>where T- это то, что вы бы вернули, если бы метод был синхронным. Идея состоит в том, что таким образом каждый вызывающий ваш метод может затем «асинхронно ждать» (я не знаю, какой для этого хороший термин) завершения базового метода Stream.ReadAsync(). Метафора для этого, которую вы можете использовать, заключается в том, что асинхронный режим «заразителен» и распространяется от низкоуровневого встроенного ввода-вывода в другой код, результаты которого зависят от результатов указанного ввода-вывода.
millimoose

Ответы:

307

asyncЭквивалент Action<T>является Func<T, Task>, таким образом , я считаю , что это то , что вы ищете:

public async Task<T> DoSomethingAsync<T>(Func<T, Task> resultBody)
    where T : Result, new()
{
  T result = new T();
  await resultBody(result);
  return result;
}
Стивен Клири
источник
@Stephen Ясно, что я пытаюсь реализовать нечто подобное в мессенджере MVVM ligth, могу ли я реализовать то же самое?
Хуан Пабло Гомес,
@JuanPabloGomez: Я не знаком с их типом обмена сообщениями, но я не понимаю, почему это не сработает.
Стивен Клири
1
Это потрясающе! Я думал, что невозможно сделать асинхронное действие, и уже считал это языковым недостатком. Я не думал об использовании Func. Спасибо.
Ноэль Видмер
2
@DFSFOT: асинхронный эквивалент voidметода - Taskметод возврата; таким образом, асинхронный эквивалент Actionis Func<Task>и асинхронный эквивалент Action<T>is Func<T, Task>. Больше информации здесь .
Стивен Клири
1
@DFSFOT: асинхронный метод должен возвращать, Taskесли он не имеет возвращаемого значения. Если он использует asyncключевое слово, то фактический Taskэкземпляр будет создан конечным автоматом, а не функцией напрямую.
Стивен Клири
-12

Итак, я считаю, что способ реализовать это:

public Task<T> DoSomethingAsync<T>(Action<T> resultBody) where T : Result, new()
{
    return Task<T>.Factory.StartNew(() =>
    {
        T result = new T();
        resultBody(result);
        return result;
    });
}
Альбин Анке
источник
8
Вам следует избегать Task.Run(и тем более StartNew) ASP.NET.
Стивен Клири
Как лучше это сделать?
Альбин Анке,
Я опубликовал ответ и проголосовал за ответ @svick. Оба хороших ответа.
Стивен Клири