Каковы различия между 3 вызовами для асинхронной функциив с #? - PullRequest
2 голосов
/ 20 сентября 2019

Каковы различия между 3 вызовами внутри метода WhatDifferences?

Вот тестовый код:

async Task WhatDifferences(Context context)
{
    await ActionAsync(context, async x => await IsOddAsync(x).ConfigureAwait(false));
    await ActionAsync(context, x => IsOddAsync(x));
    await ActionAsync(context, IsOddAsync);
}

async Task<T> ActionAsync<T>(Context context, Func<Context, Task<T>> action)
{
    return await action(context).ConfigureAwait(false);
}

async Task<bool> IsOddAsync(Context context)
{
    return await Task.Run(() => context.Count++ % 2 == 1).ConfigureAwait(false);
}

class Context
{
    public int Count { get; set; }
}

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

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

Возможно, вы знаете мою проблему, если я покажу больше логики

async Task<T> ActionAsync<T>(Context context, Func<Context, Task<T>> action)
{
    using (var transaction = new TransactionScope())
    {
        //do some async logic before action
        var result = await action(context).ConfigureAwait(false);
        //do other async validation logic after action
        return result;
    }
}

Ответы [ 2 ]

3 голосов
/ 21 сентября 2019

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

В данном конкретном случае это по существу верно.

Этот создает делегат, который ссылается на метод IsOddAsync:

await ActionAsync(context, IsOddAsync);

Этот создает метод для лямбда-выражения, а делегат ссылается на этот метод, сгенерированный компилятором:

await ActionAsync(context, x => IsOddAsync(x));

И этот делает то же самое, но для асинхронной лямбды, поэтому сгенерированный компилятором метод также имеет конечный автомат async:

await ActionAsync(context, async x => await IsOddAsync(x).ConfigureAwait(false));

В общем, ваш вопрос кипитдо двух вопросов:

  1. Должен ли я использовать группы методов вместо лямбд?Да, ты должен.В этом нет недостатка, и это чуть более эффективный, более короткий код, не влияющий на удобство сопровождения.
  2. Должен ли я исключить async / await или оставить ключевые слова в?Этот более нюансированный.

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

Однако, если ваша лямбда более сложная - выполнение операций на x перед передачей ее на IsOddAsync, иливыполняя операции с результатом или используя блок using, вы захотите сохранить ключевые слова async / await для обеспечения максимальной управляемости.Больше информации здесь .

3 голосов
/ 20 сентября 2019

await против возврата Task

Разница между:

await ActionAsync(context, async x => await IsOddAsync(x).ConfigureAwait(false));
await ActionAsync(context, x => IsOddAsync(x));

В некоторых случаях вам не нужно await (а также не asyncконечно)

Методы, выполняющие асинхронные операции, не должны использовать await, если:

  • В методе есть только один асинхронный вызов
  • Асинхронный вызов находится в конце метода
  • Исключение отлова / обработки, которое может произойти в Задаче, необязательно

См. Возврат Задачи без ожидания.

В этом случае вы можете вернуть Task промежуточно.

Обратите внимание, что существует небольшое различие в поведении - в зависимости от реализации IsOddAsync:

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

Как отметил Габриэль Люси, в текущей реализации IsOddAsync (один вызов и await) нет различий в поведении.

x => IsOddAsync(x) против IsOddAsync

Разница между

await ActionAsync(context, x => IsOddAsync(x));
await ActionAsync(context, IsOddAsync);

В первом из них вы создаете анонимный (лямбда) метод с параметром x,Поскольку IsOddAsync имеет также один параметр (с тем же типом), нет необходимости в лямбда-методе.

Обратите внимание, что вам нужна лямбда, если IsOddAsync имеет другие параметры, например, и 2-й параметр, тогдатебе нужна лямбдаПример:

await ActionAsync(context, x => IsOddAsync(x, "mySecondParameter"));

В этом случае нет различий в поведении, кроме стека вызовов при возникновении исключения внутри.

...