【问题标题】:Local functions benefits with async methods本地函数受益于异步方法
【发布时间】:2020-05-06 11:00:40
【问题描述】:

根据微软Docs:

本地函数有两种常见用例:公共迭代器方法和公共异步方法。这两种方法生成的代码报告错误的时间都比程序员预期的要晚。 ... 该技术可以与异步方法一起使用,以确保由参数验证产生的异常在异步工作开始之前抛出:

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

我不太明白这个例子。如果我们只是摆脱本地函数并将其内容提取到外部范围并在验证后立即放置,我们会不会得到相同的结果?在这种情况下,本地函数实际上给出了什么?

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

【问题讨论】:

  • 它使您能够在运行async操作之前验证数据并抛出异常,否则您只能在等待操作时观察到异常
  • 也就是说,我个人认为这个特定用例的好处是可疑的。 ArgmentException and friends should never be caught,所以它们被扔到哪里并不重要。

标签: c# .net async-await c#-7.0


【解决方案1】:

两者之间有区别:

  1. 调用PerformLongRunningWork 会抛出异常。
  2. 调用PerformLongRunningWork 并成功执行,并返回包含异常的Task&lt;string&gt;。

即:

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 中。

因此,您的示例使用非async 方法委托给async 本地函数将在调用时直接抛出那些ArgumentException,而不是将它们包裹在它返回的Task&lt;string&gt; 中。

如果您重写示例以删除本地函数并改为使用PerformLongRunningWork async,那么那些ArgumentExceptions 将被包裹在返回的Task&lt;string&gt; 中。

你想做什么是一个争论的问题。

【讨论】:

    【解决方案2】:

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

    我经常受益的另一个用例是使用TaskCompletionSource&lt;T&gt;。一个例子:

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

    【讨论】:

    • 我认为问题不是“局部函数或普通方法”,而是“为什么要有局部函数?”
    猜你喜欢
    • 2015-07-22
    • 1970-01-01
    • 1970-01-01
    • 2016-03-08
    • 2016-01-31
    • 1970-01-01
    • 2020-12-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多