c# - 我能否在同步和异步 lambda 的类似方法中避免重复代码?

标签 c# asynchronous lambda async-await

我有一个辅助方法,它需要一个 lambda 在 try/catch/finally block 中执行,例如

public void ProcessSpreadsheet(string filename, Action<object[,]> process) {
    try {
        // Open MS Excel workbook
        // Open sheet & extract data into valueArray
        // ... more boiler plate ...

        process(valueArray);
    }
    catch (FooException e) {
        LogFoo(e.Message);
        throw;
    }
    catch (BarException e) {
        LogBar(e.Message);
        throw;
    }
    finally {
        // Close workbook, release resources, etc..
    }
}

我现在想创建一个接受异步 lambda 的异步版本。

(请注意,我已阅读并同意 Stephen Toub 的 post s 关于同步之上的异步,但这不适用于此方法的症结所在,其中异步是或无益,由消费者通过 lambda 提供。)

我可以复制粘贴上面的,标记为async , 将返回类型更改为 Task ,将“Async”添加到它的名称中,更改 process 的类型Func<object[,],Task> 的参数和改变

process(valueArray);

await process(valueArray);

一切正常。但我想避免重复 try/catch/finally 中的所有代码。有没有一种无需重复代码即可实现此目的的简洁方法?

到目前为止,我得到的最佳解决方案是:

public async Task ProcessSpreadsheetAsync(string filename, Func<object[,],Task> process) {
    var asyncTaskComplete = new ManualResetEvent(false);
    ProcessSpreadsheet(filename, async valueArray => {
        await process(valueArray);
        asyncTaskComplete.Set();
    });
    await Task.Run(() => asyncTaskComplete.WaitOne());
}

但它很乱,而且没有处理异常。请问还有别的方法吗?

最佳答案

I've read and agree with Stephen Toub's posts on async over sync, but that doesn't apply here as the crux of the method, where asynchrony is or isn't beneficial, is supplied by the consumer via a lambda.

好吧,是的,不是。我可以看到你在哪里说消费代码将决定它是否是异步的。但是为了避免重复,您仍然必须提防同步优于异步和异步优于同步的反模式。

在解决方案方面,第一个想到的是接受异步lambda。这在概念上类似于在接口(interface)中定义 Task 返回方法 - 实现可能是异步的,也可能是同步的。

在您的情况下,它看起来像:

public async Task ProcessSpreadsheetAsync(string filename, Func<object[,],Task> process) {
  try {
    // Open MS Excel workbook
    // Open sheet & extract data into valueArray
    // ... more boiler plate ...

    await process(valueArray);
  }
  catch (FooException e) {
    LogFoo(e.Message);
    throw;
  }
  catch (BarException e) {
    LogBar(e.Message);
    throw;
  }
  finally {
    // Close workbook, release resources, etc..
  }
}

我会保留它。任何同步进程都可以:

await ProcessSpreadsheetAsync(filename, data => { ...; return Task.FromResult(true); });

在这种特殊情况下,您还可以像这样编写包装器:

public void ProcessSpreadsheet(string filename, Action<object[,]> process)
{
  ProcessSpreadsheetAsync(filename, async data => { process(data); }).GetAwaiter().GetResult();
}

但是,这是安全的,因为ProcessSpreadsheetAsync 只有一个await,它在其进程 上。如果 ProcessSpreadsheetAsync 被更改为具有另一个 await,则包装器很容易导致死锁。

关于c# - 我能否在同步和异步 lambda 的类似方法中避免重复代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36992062/

相关文章:

javascript - XMLHttp请求 : running more than one

linq - 如何在我的lambda表达式中使用带有2个参数的方法

c# - SemaphoreSlim 使用限制

c# - Google Drive API 上传/创建文件 (.NET)

C# - 32 位和 64 位的数学运算结果不一致

javascript - async.auto 结果对象应该有一个值,但有一个包含两个值的数组

c# - 查找目录中所有文件的更好方法

c++ - 异步 ReadDirectoryChangesW()?

android - kotlin lambda 表达式作为可选参数

c# - 是否可以创建一个通用的 Func<T><T>