[英]Using callbacks instead of throwing exceptions?
我正在考慮使用回調而不是在C#/ .NET中拋出異常。
優點是
缺點是
我可能錯過了一些關鍵的缺點,因為我想知道為什么不使用它。 我錯過了什么缺點?
代替
void ThrowingMethod() {
throw new Exception();
}
和
void CatchingMethod() {
try {
ThrowingMethod();
} catch(Exception e) {
//handle exception
}
}
我會做
void ThrowingMethod(ExceptionHandler exceptionHandler) {
exceptionHandler.handle(new Exception());
}
void CatchingMethod() {
ThrowingMethod(exception => */ handle exception */ );
}
同
delegate void ExceptionHandler(Exception exception);
在某處定義並且“handle(...)”是一個檢查null的擴展方法,檢索堆棧跟蹤,如果在拋出異常時根本沒有異常處理程序,則可能拋出“UnhandledException”。
void UsedToNotThrowButNowThrowing() {
UsedToNotThrowButNowThrowing(null);
}
//overloads existing method that did not throw to now throw
void UsedToNotThrowButNowThrowing(ExceptionHandler exceptionHandler) {
//extension method "handle" throws an UnhandledException if the handler is null
exceptionHandler.handle(exceptionHandler);
}
TResult ThrowingMethod(ExceptionHandler<TResult> exceptionHandler) {
//code before exception
return exceptionHandler.handle(new Exception()); //return to interrupt execution
//code after exception
}
TResult CatchingMethod() {
return ThrowingMethod(exception => */ handle exception and return value */ );
}
同
delegate TResult ExceptionHandler<TResult>(Exception exception);
首先,您需要將這些處理程序的開銷傳遞給應用程序中的幾乎所有方法。 這是一個非常重量級的依賴關系,以及在構建應用程序之前做出的決定。
其次,存在處理系統拋出異常和來自第三方程序集的其他異常的問題。
第三,異常是指在拋出程序時暫停程序的執行,因為它確實是“異常”,而不僅僅是可以處理的錯誤,允許繼續執行。
可擴展性。
正如@mungflesh正確地指出你必須通過這些處理程序 。 我的第一個問題不是開銷而是可伸縮性 :它會影響方法簽名。 它可能會導致與我們在Java中使用已檢查異常相同的可伸縮性問題(我不知道C#,我只做C ++和一些Java)。
想象一下一個深度為50次調用的調用堆棧(沒有什么極端的東西,IMO)。 有一天,一個變化出現了,並且沒有拋出的鏈中的一個被調用者變成了一個現在可以拋出異常的方法。 如果它是未經檢查的異常,您只需更改頂級代碼即可處理新錯誤。 如果它是已檢查的異常或您應用了您的想法,則必須通過調用鏈更改所有涉及的方法簽名。 不要忘記簽名更改傳播:您更改這些方法的簽名,您必須在調用這些方法的其他地方更改代碼,可能會生成更多簽名更改。 簡而言之,規模很差。 :(
這是一些偽代碼,顯示了我的意思。 使用未經檢查的異常,您可以通過以下方式處理深度為50的callstack中的更改:
f1() {
try { // <-- This try-catch block is the only change you have to make
f2();
}
catch(...) {
// do something with the error
}
}
f2() { // None of the f2(), f3(), ..., f49() has to be changed
f3();
}
...
f49() {
f50();
}
f50() {
throw SomeNewException; // it was not here before
}
使用您的方法處理相同的更改:
f1() {
ExceptionHandler h;
f2(h);
}
f2(ExceptionHandler h) { // Signature change
f3(h); // Calling site change
}
...
f49(ExceptionHandler h) { // Signature change
f50(h); // Calling site change
}
f50(ExceptionHandler h) {
h.SomeNewException(); // it was not here before
}
所涉及的所有方法(f2 ... f49)現在都有一個新簽名,並且呼叫站點也必須更新(例如f2()變為f2(h)等)。 請注意, f2...f49
甚至不應該知道這個更改,但是,他們的簽名和調用站點都必須更改。
換句話說:所有中間調用現在必須處理錯誤處理程序,即使它是一個他們甚至不知道的細節。 使用未經檢查的異常,可以隱藏這些詳細信息。
未經檢查的異常確實“隱藏得像控制流”,但至少它們可以很好地擴展。 毫無疑問,可以迅速導致難以維護的混亂......
雖然+1,一個有趣的想法。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.