Преимущества локальных функций с помощью асин c методов - PullRequest
0 голосов
/ 20 января 2020

Согласно Microsoft Документы :

Существует два распространенных варианта использования локальных функций: publi c методы итератора и publi c asyn c методы , Оба типа методов генерируют код, который сообщает об ошибках позже, чем могут ожидать программисты. ... Технику можно использовать с асинхронными c методами, чтобы гарантировал, что исключения , возникающие из проверки аргумента , будут выданы до начала асинхронной работы :

public Task<string> PerformLongRunningWork(string address, int index, string name)
{
    if (string.IsNullOrWhiteSpace(address))
        throw new ArgumentException(message: "An address is required", paramName: nameof(address));
    if (index < 0)
        throw new ArgumentOutOfRangeException(paramName: nameof(index), message: "The index must be non-negative");
    if (string.IsNullOrWhiteSpace(name))
        throw new ArgumentException(message: "You must supply a name", paramName: nameof(name));

    return longRunningWorkImplementation();

    async Task<string> longRunningWorkImplementation()
    {
        var interimResult = await FirstWork(address);
        var secondResult = await SecondStep(index, name);
        return $"The results are {interimResult} and {secondResult}. Enjoy.";
    }
}

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

Если она на самом деле ничего не улучшает, вы можете придумать лучший пример?

Ответы [ 2 ]

4 голосов
/ 20 января 2020

Существует разница между:

  1. Вызов PerformLongRunningWork, и он вызывает исключение.
  2. Вызов PerformLongRunningWork, и он успешно выполняется и возвращает Task<string> который содержит исключение.

То есть:

Task<string> task;
try
{
    task = PerformLongRunningOperation();
}
catch (Exception e)
{
    // PerformLongRunningOperation itself threw
}

bool containsException = task.IsFaulted;

try
{
    string result = await task;
}
catch (Exception e)
{
    // The Task<string> returned from PerformLongRunningWork contained an exception
}

Если вы выбросите исключение из метода async Task, это исключение будет заключено в Task, который

Следовательно, ваш пример с не-1019 * методом, который делегирует локальной функции async, будет выбрасывать эти ArgumentException непосредственно при вызове, и не будет возвращать их в Task<string> это возвращается.

Если вы переписали пример для удаления локальной функции и вместо этого сделали PerformLongRunningWork async, тогда эти ArgumentExceptions будут заключены в возвращаемом Task<string>.

Какой из них Вы хотите сделать, это вопрос дискуссии.

0 голосов
/ 20 января 2020

Одно из преимуществ: мне не нужно передавать локальные переменные и параметры в качестве параметров другому методу, поскольку они уже захвачены.

Другой вариант использования, который мне часто помогает, - это работа с TaskCompletionSource<T>. Пример тому:

public Task<T> DoSomeWorkAsync()
{
    TaskCompletionSource<T> completionSource = new TaskCompletionSource<T>();
    SomeBackgroundWorker worker = new SomeBackgroundWorker();
    worker.OnWorkComplete += workComplete;
    worker.DoSomeWorkInABackgroundThread();
    return completionSource.Task;

    void workComplete(T workResult)
    {
        worker.OnWorkComplete -= workComplete;
        completionSource.SetResult(workResult);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...