本地函数受益于异步方法

Local functions benefits with async methods

根据微软 Docs:

There are two common use cases for local functions: public iterator methods and public async methods. Both types of methods generate code that reports errors later than programmers might expect. ... The technique can be employed with async methods to ensure that exceptions arising from argument validation are thrown before the asynchronous work begins:

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.";
    }
}

我不是很理解这个例子。如果我们只是去掉局部函数,将其内容提取到外部作用域中,并在验证后立即将它们放入,我们是否会得到相同的结果?在这种情况下,局部函数实际上给出了什么?

如果它实际上没有改善任何东西,你能想出一个更好的例子吗?

有区别:

  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 中,即 returned.

因此,您使用非 async 方法委托给 async 局部函数的示例将在调用时直接抛出那些 ArgumentException,而不是 return他们包裹在 Task<string> 它 return 中。

如果您重写示例以删除局部函数并改为生成 PerformLongRunningWork async,那么那些 ArgumentExceptions 将被包裹在 Task<string> [=42] 中=]ed.

你想做哪一个是一个有争议的问题。

其中一个好处:我不必将局部变量和参数作为参数传递给其他方法,因为它们已经被捕获。

我经常受益的另一个用例是使用 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);
    }
}