Як ви реалізуєте метод делегування асинхронних дій?


132

Трохи довідкової інформації.

Я вивчаю стек Web API і намагаюся інкапсулювати всі дані у вигляді об’єкта "Результат" з такими параметрами, як Success та ErrorCodes.

Однак різні методи давали б різні результати та коди помилок, але, як правило, об'єкт результатів, як правило, був би створений однаково.

Щоб заощадити деякий час, а також дізнатися більше про можливості асинхронізування / очікування в 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 / wait?

Це те, що я спробував:

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, вам потрібно лише розповсюджувати asyncness з інших методів, які ви використовуєте.
мільйозу

Вибачте, я все ще досить нове в цьому, що ви маєте на увазі, коли ви говорите, що вам потрібно лише пропагувати, і що нового T-го стосується?
Альбін Анке

Я думаю, я це зрозумів, дякую мільйонному, що ти дав мені щось подумати.
Альбін Анке

1
Чому ви навіть намагаєтеся зробити цю асинхронізацію? Частіше в ситуаціях, що не перебувають у веб-серверах, фальшива асинхронізація, вкладаючи синхронний код у завдання (як ви намагаєтеся), відбувається повільніше, ніж просто робити це синхронно.
Скотт Чемберлен

1
@AlbinAnke Під "розповсюдженням" я маю на увазі, що якщо ви викликаєте метод .NET, як Stream.ReadAsync()у методі, той метод повинен бути асинхронним, а повернути Task<T>де Tте, що ви повернули б, був метод синхронним. Ідея полягає в тому, що таким чином кожен абонент вашого методу може потім "асинхронно зачекати" (я не знаю, що для цього хороший термін) для того, Stream.ReadAsync()щоб завершити основу . Метафорою для цього, яку ви можете скористатися, є те, що асинхроніка є "інфекційною" і поширюється від вбудованого вводу / виводу низького рівня в інший код, результати якого залежать від результатів зазначеного вводу / виводу.
мільйозу

Відповіді:


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 Messenger, чи можу я реалізувати так само?
Хуан Пабло Гомес

@JuanPabloGomez: Я не знайомий з їх видами обміну повідомленнями, але не розумію, чому це не вийде.
Стівен Клірі

1
Це дивно! Я думав, що неможливо зробити асинхронну дію, і вже вважав це недоліком мови. Я не думав про використання Func. Дякую.
Ноель Відмер

2
@DFSFOT: Еквівалент асинхронного voidметоду - Taskметод повернення; таким чином, асинхронний еквівалент Actionє Func<Task>, а асинхронний еквівалент Action<T>є Func<T, Task>. Більше інформації тут .
Стівен Клірі

1
@DFSFOT: метод асинхронізації повинен повертатися, Taskколи він не має значення повернення. Якщо він використовує asyncключове слово, фактичний Taskекземпляр буде створений машиною стану, а не безпосередньо функцією.
Стівен Клірі

-11

Тому я вважаю, що спосіб цього здійснити:

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;
    });
}

7
Вам слід уникати Task.Run(і тим більше StartNew) на ASP.NET.
Стівен Клірі

Який кращий спосіб зробити це?
Альбін Анке

Я опублікував відповідь і також схвалив відповідь @ svick. Вони обидва хороші відповіді.
Стівен Клірі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.