C# 对于同步和异步lambda,我可以避免类似方法中的重复代码吗?
我有一个helper方法,它在try/catch/finally块中执行lambda,例如C# 对于同步和异步lambda,我可以避免类似方法中的重复代码吗?,c#,asynchronous,lambda,async-await,C#,Asynchronous,Lambda,Async Await,我有一个helper方法,它在try/catch/finally块中执行lambda,例如 public void ProcessSpreadsheet(string filename, Action<object[,]> process) { try { // Open MS Excel workbook // Open sheet & extract data into valueArray // ... more
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..
}
}
到
一切正常。但是我希望避免重复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());
}
公共异步任务进程SpreadsheetAsync(字符串文件名,Func进程){
var asyncTaskComplete=新手动重置事件(错误);
ProcessSpreadsheet(文件名,异步valueArray=>{
等待进程(valueArray);
asyncTaskComplete.Set();
});
等待任务。运行(()=>asyncTaskComplete.WaitOne());
}
但它很混乱,而且没有处理异常。我想知道是否还有别的办法?我会放弃接受lambda的想法。这是在你的代码中接受真正不属于你的责任。相反,只需提供一个如下所示的重载:
public object[,] ProcessSpreadsheet(string filename) {
try {
// Open MS Excel workbook
// Open sheet & extract data into valueArray
// ... more boiler plate ...
return valueArray;
}
catch (FooException e) {
LogFoo(e.Message);
throw;
}
catch (BarException e) {
LogBar(e.Message);
throw;
}
finally {
// Close workbook, release resources, etc..
}
}
public void ProcessSpreadsheet(string filename, Action<object[,]> process)
{
ProcessSpreadsheetAsync(filename, async data => { process(data); }).GetAwaiter().GetResult();
}
如果同步代码正在使用它,它只需执行以下操作:
try {
var results = ProcessSpreadsheet("...");
DoSomethingElse(results);
} catch (FooException e) {
// ...
} catch (CanHappenWhileDoingSomethingElseException e) {
// ...
}
就处理异常而言,代码的使用者具有无限的灵活性
如果有人正在使用async/await,他们所要做的就是:
try {
var results = await Task.Run(() => ProcessSpreadsheet("..."));
DoSomethingElse(results);
} catch (FooException e) {
// ...
} catch (CanHappenWhileDoingSomethingElseException e) {
// ...
}
他们所有的错误处理方式都完全相同
我已经阅读并同意Stephen Toub关于async over sync的帖子,但这并不适用于这里,因为该方法的关键在于,异步是有益还是有害,是由消费者通过lambda提供的
好的,是和否。我可以看到你会说消费代码将决定它是否是异步的。但是为了避免重复,您仍然必须注意sync over async和async over sync反模式
就解决方案而言,首先想到的是只接受异步lambda。这在概念上类似于在接口中定义任务
-返回方法-实现可能是异步的,也可能是同步的
在您的情况下,它看起来像:
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..
}
}
在这种情况下,您还可以编写如下包装:
public object[,] ProcessSpreadsheet(string filename) {
try {
// Open MS Excel workbook
// Open sheet & extract data into valueArray
// ... more boiler plate ...
return valueArray;
}
catch (FooException e) {
LogFoo(e.Message);
throw;
}
catch (BarException e) {
LogBar(e.Message);
throw;
}
finally {
// Close workbook, release resources, etc..
}
}
public void ProcessSpreadsheet(string filename, Action<object[,]> process)
{
ProcessSpreadsheetAsync(filename, async data => { process(data); }).GetAwaiter().GetResult();
}
public void ProcessSpreadsheet(字符串文件名,操作流程)
{
ProcessSpreadsheetAsync(文件名,异步数据=>{process(data);}).GetAwaiter().GetResult();
}
但是,这只是安全的,因为
ProcessSpreadsheetAsync
只有一个wait
,它位于其进程上。如果将ProcessSpreadsheetAsync
更改为另一个等待
,则包装器很容易导致死锁。要回答实际问题,而不是告诉您执行其他操作,只需将try/catch块提取到函数中即可。这通常被建议作为关注点分离的一部分。乱扔逻辑和错误处理是不好的形式。您试图避免重复哪些代码?try/catch/finally代码?为什么要通过任务运行代码。在异步版本中运行?使用异步版本,你到底想实现什么?@YacoubMassad是的,应该是Func,我已经解决了,谢谢。这段代码真的有效吗?process
抛出的异常是否会被catch块捕获?是的,我希望避免重复try/catch/finally代码。对于传入的lambda将从异步中受益的情况,我想要一个异步版本。例如,我使用同步版本来计算电子表格中的总计,但我希望使用异步版本,在异步版本中,我对电子表格中的每一行进行网络调用。这会很慢,我想让UI保持响应,所以我想在异步lambda中执行。@YacoubMassad,不,你又是对的,没有捕获异常:(.我想我需要比我想象的更多的帮助。在这里复制代码不是比强制同步方法异步运行更好吗?因为这会增加堆上额外分配的成本。@NazmiAltun:同步方法仍然同步运行;它们返回一个已完成的任务。如果你想避免分配,你可以为任务定义一个静态
字段。从结果(true)
或使用值任务
。
public void ProcessSpreadsheet(string filename, Action<object[,]> process)
{
ProcessSpreadsheetAsync(filename, async data => { process(data); }).GetAwaiter().GetResult();
}