异步/等待 Lambda表达式

4
我有一个奇怪的问题,涉及async/await的使用: 我创建了一个小程序,基本上处理每个操作的try/catch:
    internal static void HandledAction(Action action, Info infoBar)
    {
        try
        {
            action();
        }
        catch (Exception ex)
        {
            infoBar.SetError("An Exception occured: " + ex.Message);
            WriteLog(ex.StackTrace);
        }

没有什么花里胡哨的,但它值得一试,因为更改错误处理非常容易。 但是,如果我想在Lambda中异步获取数据会发生什么?让我们看一个简单的例子:

    private void mnuImportData_Click(object sender, RoutedEventArgs e)
    {
        ActionHelper.HandledAction(async () =>
        {
            throw new NotImplementedException("Ups");
        }, infoMain);
    }

当然,HandledAction得到调用并通过,因为它收到指针并抛出异常,但未被处理。
我想我必须创建一个AsyncHandledAction,并将该操作设置为异步执行,但是是否有更简单的方法来解决这个问题?
我猜很多人使用中央异常处理,并且有更好的解决方案吗?
提前感谢!
马蒂亚斯
编辑:我创建了一个示例,应该展示我需要的内容:基本上,我不想使我传递的整个操作变为可等待的,而是在Lambda中的一个调用:
ActionHelper.HandledActionAsync(() =>
        {
            //elided
            CheckFileResult rslt = await excelImport.CheckFilesAsync(tmpPath);
            //elided
        }, infoMain);

当然,这样做会出现错误:

错误 3:'await' 操作符只能在异步 lambda 表达式中使用。请考虑使用 'async' 修饰符标记此 lambda 表达式。


2
async () => { } 可以匹配 ActionFunc<Task> 两种类型。如果匹配到前者,就像有一个方法签名为 async void F();后者则是 async Task F()。在你的情况下,你得到的是 async void F(),它具有“fire-and-forget”语义。 - Timothy Shields
2个回答

7
原因是: 使用Action而不是Func。 因为您的代码:
async () =>
        {
            throw new NotImplementedException("Ups");
        }

实际上是:

async void Method() { }

Func<Task> 是:

async Task Method() { }

异步的 void 方法会捕获 SynchronizationContext.Current,当出现异常时,它将通过 SynchronizationContext.Post() 发送到 SynchronizationContext 中(在 windows runtime 中可以捕获这些类型的异常)。对于 ASP.NET/Console applicationSynchronizationContext.Current 返回 null,这意味着异常会传播到线程池中,我不确定,但我认为无法捕获它。然而,如果异步方法返回了 Task,则异常可以通过返回的 Task 被传递给调用者。值得一提的是,异步 lambda 表达式总是优先选择带有 Func<Task> 的方法,而不是 Action。通常的规则是:除非它是“顶级方法”(例如事件处理程序),否则永远不要使用 async voidasync Action)。


我认为这不是什么大问题,因为整个事情的想法就是处理所有的异常。 - Matthias Müller
除非您拥有Func<Task> / async Task方法,否则您无法以您目前的方式捕获任何异常。 - fex
我想我明白了,就像Stilgar所说的那样,我基本上需要将异步映射到调用,并且我需要一个Func<Task>来获取异常,到目前为止谢谢。 - Matthias Müller

2

您需要一个HandleAction的异步版本

internal static async Task HandledAction(Func<Task> action, Info infoBar)
{
    try
    {
        await action();
    }
    catch (Exception ex)
    {
        infoBar.SetError("An Exception occured: " + ex.Message);
        WriteLog(ex.StackTrace);
    }
}

当然,你应该使用await调用该方法。
private async void mnuImportData_Click(object sender, RoutedEventArgs e)
{
    await ActionHelper.HandledAction(async () =>
    {
        throw new NotImplementedException("Ups");
    }, infoMain);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接