2016-05-02 35 views
2

我有一個幫助方法,它需要在try/catch/finally塊中執行lambda,例如我是否可以避免在類似方法中使用同步和異步lambdas的重複代碼?

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的異步版本。

(注意,我已經閱讀並與斯蒂芬Toub的posts異步過同步同意,但這並不適用於這裏的方法,其中不同步是或不是有益的癥結,提供通過經拉姆達消費者。)

我可以複製並粘貼以上,將其標記爲async,改變返回類型爲Task,加上「異步」,以它的名字,改變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()); 
} 

,但它的混亂和異常不處理。我想知道是否有另一種方式?

+0

您試圖避免重複的代碼是? try/catch/finally代碼?你爲什麼通過異步版本中的'Task.Run'運行代碼?你試圖用異步版本實現什麼功能? –

+0

@YacoubMassad是的,這應該是Func ,我修正了,謝謝。 – eoinmullan

+0

這段代碼真的有用嗎?被'process'拋出的異常被catch塊捕獲了嗎? –

回答

0

我會放棄接受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.. 
    } 
} 

如果這是由同步代碼消耗,它只會做:

try { 
    var results = ProcessSpreadsheet("..."); 
    DoSomethingElse(results); 
} catch (FooException e) { 
    // ... 
} catch (CanHappenWhileDoingSomethingElseException e) { 
    // ... 
} 

代碼的消費者有無限的靈活性遠就處理例外而言。

如果有人使用異步/的await,所有他們需要做的是:

try { 
    var results = await Task.Run(() => ProcessSpreadsheet("...")); 
    DoSomethingElse(results); 
} catch (FooException e) { 
    // ... 
} catch (CanHappenWhileDoingSomethingElseException e) { 
    // ... 
} 

他們所有的錯誤處理的工作原理完全一樣。

1

我已閱讀並同意Stephen Toub在異步同步上的帖子,但這不適用於此處作爲方法的關鍵,其中異步是或不是有益的,由消費者通過拉姆達。

嗯,是的,沒有。我可以看到你會說消費代碼將決定它是否是異步的。但爲了避免重複,您仍然需要注意異步同步和異步同步反模式。

在解決方案方面,首先想到的是只有接受異步lambdas。這在概念上類似於在接口中定義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,這是它的process。如果ProcessSpreadsheetAsync被更改爲具有另一個await,則包裝可能很容易導致死鎖。

1

要回答你的實際問題,而不是告訴你做別的事情,只需將try/catch塊提取到函數中即可。這通常被建議作爲問題分離的一部分。亂拋垃圾邏輯與錯誤處理是不好的形式。

相關問題