為什麼不建議用 try catch?


不問是不是,就問為什麼。這個問題看來需要從頭說起。

一句話解釋:

try catch機制非常好。那些覺得try catch不行的人,是他們自己的水平有問題,無法理解這種機制。並且這群人寫代碼不遵守規則,喜歡偷懶,這才造成try catch不好的錯覺。

詳細解釋:

1.程序要健壯,必須要設計報錯機制。

最古老,也是最常見的,比如:

bool CreateFile( );

//如果創建文件失敗就返回false,否則返回true。

這種報錯方式,顯然不好。因為它沒有給出產生錯誤的具體原因。

2.改進:一個函數或過程,會因為不同的原因產生錯誤,報錯機制必須要把這些錯誤原因進行區分後,再彙報。

比如:

int CreateFile():

//如果創建成功就返回1.

//如果是因為沒有許可權,導致失敗,返回-1。

//如果是因為文件已經存在,導致失敗,返回-2。

//如果是因為創建文件發生超時,導致失敗,返回-3。

這樣看上去,比【1】要好些,至少指出了比較具體的失敗原因,但是,還不夠。

3.很多情況下,函數需要把詳細的原因,用字元串的方式,返回:

class Result

{

....int State;//同【2】

....string ErrorMessage;//如果失敗,這裡將給出詳細的信息,如果有可能,應該把建議也寫上去。

}

Result CreateFile();

//如果創建成功,返回的Result,State為1,ErrorMessage為null。

//如果是因為沒有許可權,導致失敗,返回的Result,State為-1,ErrorMessage為"用戶【guest】沒有許可權在【C:】這個目錄下創建該文件。建議您向管理員申請許可權,或者更換具有許可權的用戶。"。

//如果是因為文件已經存在,導致失敗,返回的Result,State為-2,ErrorMessage為"文件【C:abc.txt】已經存在。如果需要覆蓋,請添加參數:arg_overwrite = true"。

//如果是因為創建文件發生超時,導致失敗,返回的Result,State為-3,ErrorMessage為"在創建文件時超時,請使用chkdsk檢查文件系統是否存在問題。"。

4.我個人推崇上面這種方式,完整,美觀。但是這種流程,容易與正常的代碼混在一起,不好區分開。因此,Java、C#等設計了try catch這一種特殊的方式:

void CreateFile()

//如果創建成功就不會拋出異常。

//如果是因為沒有許可權,導致失敗,會拋出AccessException,這個Exception的Msg屬性為"用戶【guest】沒有許可權在【C:】這個目錄下創建該文件。建議您向管理員申請許可權,或者更換具有許可權的用戶。"。

//如果是因為文件已經存在,導致失敗,會拋出FileExistedException,這個Exception的Msg屬性為"文件【C:abc.txt】已經存在。如果需要覆蓋,請添加參數:arg_overwrite = true"。

//如果是因為創建文件發生超時,導致失敗,會拋出TimeoutException,這個Exception的Msg屬性為"在創建文件時超時,請使用chkdsk檢查文件系統是否存在問題。"。

可見,上述機制,實際上是用不同的Exception代替了【3】的State。

這種機制,在外層使用時:

try

{

....CreateFile( "C:abc.txt" );

}

catch( AccessException e )

{

....//代碼進入這裡說明發生【沒有許可權錯誤】

}

catch( FileExistedException e )

{

....//代碼進入這裡說明發生【文件已經存在錯誤】

}

catch( TimeoutException e )

{

....//代碼進入這裡說明發生【超時錯誤】

}

對比一下【3】,其實這與【3】本質相同,只是寫法不同而已。

5.綜上,我個人喜歡【3】這類面向過程的寫法。但很多喜歡面向對象的朋友,估計更喜歡【4】的寫法。然而【3】與【4】都一樣。這兩種機制都是優秀的錯誤處理機制。

6.理論說完了,回到正題,題注問:為什麼不用try catch?

答:這是因為,很多菜鳥,以及新手,他們是這樣寫代碼的:

void CreateFile( )

//無論遇到什麼錯誤,就拋一個 Exception,並且也不給出Msg信息。

這樣的話,在外層只能使用:

try

{

....CreateFile( "C:abc.txt" );

}

catch( Exception e )

{

....//代碼進入這裡說明發生錯誤

}

當出錯後,只知道它出錯了,並不知道是什麼原因導致錯誤。這同【1】。

以及,即使CreateFile是按【4】的規則設計的,但菜鳥在外層是這樣使用的:

try

{

....CreateFile( "C:abc.txt" );

}

catch( Exception e )

{

....//代碼進入這裡說明發生錯誤

....throw Exception( "發生錯誤" )

}

這種情況下,如果這位菜鳥的同事,調用了這段代碼,或者用戶看到這個錯誤信息,也只能知道發生了錯誤,但並不清楚錯誤的原因。這與【1】是相同的。

出於這些原因,菜鳥的同事,以及用戶,並沒有想到,造成這個問題是原因菜鳥的水平太差,寫代碼圖簡單省事。他們卻以為是try catch機制不行。

因此,這就導致了二逼同事,以及傻比用戶,不建議用try catch。


剛回答完阿里禁join的,又看到了一個按圖索驥的,這次我手動P個圖給你好了。


Catch之後你認真,實在的處理了么?真需要處理且實際處理了可預知的異常,那可以用。否則別用。那些try{一坨代碼}catch{}的,以及try{一坨代碼}catch(Exception ex){throw ex;}的碼農們,省省力氣,少打幾個字不好么?


最好問清楚是那種語言。不同語言處理Try Catch的機制不一樣,所以也會有不同回答。

比如C++,是不推薦用try catch的,它推薦使用Windows API那種HResult來返回錯誤情況,原因是try catch會在已有的代碼上面增加額外的cost, 這個額外的cost不是說只有throw exception的時候才會有,而是在try catch block裡面的每一行代碼中都會有,這也是為什麼他不建議你使用try catch最主要的原因。在Windows的源代碼中,是沒有任何try catch的,全部用HResult來處理。

比如C#, try catch是建議使用的,C#設計的時候吸取的C++ try catch的教訓,所以直接用Try catch包裹已有代碼增加的cost可以忽略不計,但是如果真的在代碼運行過程中throw exception了,這個cost還是很大的。所以,在C#代碼設計中,throw exception基本上是你認為不會發生這種意外的情況下,否則,如果是常見錯誤,最好不要throw exception。

比如Java, try catch也是建議使用的,我這個用的不熟,不過看它的說明,即使是throw exception的時候的cost也很小。

總結說來,try catch是否建議使用要看具體語言,最重要衡量的標準就是它對已有的代碼性能有多大的影響。但是從它設計的角度就是為了處理一些意料不到的情況,但是因為當初引入的時候各種各樣的原因,導致有些語言為了性能,不推薦使用。

BTW, try catch最好不要catch (Exception), 這樣會吃掉不該吃的問題,比如C#中的StackOverflowException, OutOfMemoryException, NullReferenceException etc. 該crash的時候就應該讓App crash, restart, 這也是保護你service的一個好方法。


要是你可以保證Catch到異常之後能夠合理的處置他,請盡量用try - catch保證程序的健壯性,如果只是提示一個XXX發生了錯誤,還不如讓系統處理(廣告:或者用我的ShowError庫OwO)

主要是碰到亂抓異常的根本沒法調試啊喂!不報錯,不崩潰就是數據不對…只能一行行的去單步…想死的心都有了

(強行廣告 -

ShowError https://github.com/VeroFess/ShowError

原理:http://blog.binklac.com/2017-03-16-SEH.html


只有錯誤可預知時才用try。所有不可預知的錯誤用try都是不負責任的寫法


程序員平均水平,沒法保證用異常的話不會寫出翔代碼噁心別人,尤其是C++,異常還要保證異常安全,異常安全還有三種等級,要配合RAII寫出異常安全的代碼,事務回滾的時候還要保證性能,門檻超高,索性就自我閹割掉不去用(比如Google)


異常使用誤區

第一,對錯誤進行異常處理。所謂錯誤就是,這裡本來不該發生,你卻讓它發生了,比如傳入空指針,數組越界,除數為零。這種時候正確的做法是加斷言,或者什麼也不做。處理這種錯誤是自作多情而不負責任的,你知道他為啥錯了你就給他處理?你處理了只是把鴕鳥腦袋埋進沙子,問題依然存在。底層庫沒必要為上層程序員的邏輯錯誤擦屁股。

第二,認為異常比返回錯誤碼更安全。異常可以直接跳過多層,它必然不如一層層處理安全。如果不配合自動垃圾回收,跳出多層很難保證內存不泄漏,而有gc的語言也難以避免資源泄露。因此,不建議處理異常直接跳出多層函數。

那麼異常就沒用了嗎?當然不是。異常比錯誤碼能夠攜帶更直觀的信息,高級語言應該使用異常代替錯誤碼,同時不觸及以上誤區,則可使程序更健壯。


如果說把排除bug看做警察破案的過程,不寫trycatch破案的難度就會增加,寫了會減少。

而且在一些介面項目中尤為重要,甚至要把錯誤記錄到可查詢的日誌。

因為你想,比如大宗金錢交易介面,典型的如攜程信用卡付款介面事件,沒有日誌記錄,出現了bug或者問題,這時候你就會對哪個當初不寫trycatch的程序員想付諸武力甚至想追究其法律責任。

當然,有個同事的項目,把出現的任何錯誤給用戶看都報成網路錯誤,這樣用戶就不能說他程序有問題了………然後網管成了拉仇恨的人(┐(′~`;)┌)


很多人都有一個誤區,說不用try-catch就是不用throw,那當然是不對的。throw要用,catch不能用。


基於try catch的異常處理機制,比較靈活,比較強大,也比較重。事物都有雙面性。

try catch 的代價比較大。相對於判斷返回值,跑出異常到捕獲,需要更多的cpu指令和代碼,比如回溯stack檢查是否有異常捕獲。具體如何實現,可以研究下。

try catch的好處是什麼?可以攜帶完整的出錯信息。比如出處,錯誤類型,錯誤的具體原因等。

此外異常還可逆調用鏈條傳遞,對異常處理來說可以靈活,即你可以不處理而直接告訴上層。但是應該的自己關心的異常在本層處理掉。同時可以對此異常封裝便於紀錄根本出錯,然後重新拋出。這些你返做會很累而且不清晰。

對於編程實踐,我覺得,異常應該少用,除非是這種錯誤會很少出現,即,一旦出現是一種嚴重的錯誤,那麼拋出異常是合理的。否則用返回碼來標誌。

明顯異常是一種不可或缺的編程元素,是將編程中「錯誤」進行更加精密和正規對待的方式。認識到錯誤也是編程的重要組成,而不僅僅是附屬品。這很關鍵.

任何範式和特性都不能濫用,都不能依賴語言而進行懶惰式,逃避式編程。

要寫出優秀的程序,是要斟酌和將語言特性視為工具,擇而用之。


有啊,Eff 啊

不過現在 EB 這傢伙又棄坑 Effects 做 ST 了


gnu c++在未觸發throw的時候try catch不會有額外性能開銷,-fno-exception不會帶來可見的性能提升。

java,c#,python中異常已經是個遍布各個庫的基礎語言機制了,不使用異常的話除了hello world啥都寫不了。

不推薦使用異常的原因和不推薦使用繼承的原因一樣,畢竟是個增加複雜度的語法,多多少少需要有點經驗才能正確設計和使用。

c++中需要手動釋放內存,使用異常會增加內存管理的複雜度,所以一般不用。


沒必要每個方法都Try Catch。但是為了避免服務Crash,在頂層調用還是有必要Catch的。Catch完記得打好日誌,記錄詳細的輸入輸出,異常原因等,然後通過異常報警通知給開發。


當你急著下班跟妹子約會的時候,你會發現try catch其實是個好東西,


淘寶前端團隊-溪夏已經講的很清楚了:

總結:

  • 使用 try catch 的使用無論是在 try 中的代碼還是在 catch 中的代碼性能消耗都是一樣的。
  • 需要注意的性能消耗在於 try catch 中不要直接塞進去太多的代碼(聲明太多的變數),最好是吧所有要執行的代碼放在另一個 function 中,通過調用這個 function 來執行。

針對第二點,可以查看 ECMA 中關於 try catch 的解釋,在代碼進入 try catch 的時候 js引擎會拷貝當前的詞法環境,拷貝的其實就是當前 scope 下的所有的變數。

建議

在使用 try catch 的時候盡量把 try catch 放在一個相對乾淨的 scope 中,同時在 try catch 語句中也盡量保證足夠少的變數,最好通過函數調用方式來 try catch。

原文:try catch 對代碼運行的性能影響


CppCoreGuidelines:

[E.18: Minimize the use of explicit `try`/`catch`](#Re-catch)

這才是正確處理 C++ 異常的方式啊……


有什麼資料說不建議用 try catch ? 我認為 try catch 很常用, 而且有些時候必須要用...當然 catch 住了不作任何補救措施和任何操作是要避免的...

有朋友說建議不捕獲異常直接讓 程序 duang 一下停掉, 這個要分異常類型的, 像 Java 分 check exception 和 unchecked exception, nodejs 分 operational errors 和 programmer errors, 還要結合場景看異常是否可以補救 等...

比如有些異常是可以補救的, 如連接某個伺服器, 連接失敗的話, 捕獲住異常可以隔幾秒再嘗試連接.再比如 nodejs, 如果是屬性名拼錯這種明顯的bug, 造成各種 undefined ,null 這種異常, 是不建議 try catch 的, 因為這是明顯的 bug, 沒辦法補救, 補救了反而會造成錯誤的系統狀態, 這種情況讓系統當機並修復bug更合適.

再比如 java 中的 try catch, 你可以捕獲低層異常轉譯成高層異常, 簡單說就是轉譯成信息更詳細或更容易讓客戶端理解的高層異常

當然罵異常機制的人也是有的, 比如 Java 的 checked exception 就被人罵過, 編譯器強制要求必須 try catch, 而且還是成坨成坨的....也有設計不合理的地方, 但我認為 try catch 還是很常用的.

------------------------

nodejs 的異常稍有了解, 確實 try catch 用的場景不多, 不過有個重要的原因是 nodejs 太多非同步太多 callback, 非同步調用的情況下 try catch 再 throw 是沒效果的, 所以 nodejs 還有另外兩種傳遞異常的方式 : 1. 利用 callback 2. 利用 EventEmitter 這種方式

但像 Java 以及其它部分語言.... 除了 try catch throw 還有什麼辦法補救異常和傳播異常???


不是不建議用.而是不應該濫用.

這裡《Effective Java》的有關異常的幾個條目說的非常明白.建議題主去看一下.我這裡就簡單的搬運一下.部分原文如下:

只針對不正常的情況才使用異常

書中給出了一個例子

try {
int i=0;
while (true) {
arr[i]=0;
i++;
}
} catch (IndexOutOfBoundsException e) {
}

這是一個非常明顯的錯誤,希望通過異常來中斷循環而達到某種優化.其實不然.

  • JVM對異常的塊中的代碼幾乎不做優化.

  • 而且創建、拋出、捕獲異常都是十分昂貴的

  • 正常的遍曆數組並不會造成冗餘的邊界檢查,現代的JVM會做出優化

避免不必要的使用被檢查的異常

「被檢查的異常」是Java語言的一個很好的特性。與返回代碼不同,"被檢查的異常"會強迫程序員處理例外的條件,大大提高了程序的可靠性。

  但是,過分使用被檢查異常會使API用起來非常不方便。如果一個方法拋出一個或多個被檢查的異常,那麼調用該方法的代碼則必須在一個或多個catch語句塊中處理這些異常,或者必須通過throws聲明拋出這些異常。 無論是通過catch處理,還是通過throws聲明拋出,都給程序員添加了不可忽略的負擔。

適用於"被檢查的異常"必須同時滿足兩個條件:第一,即使正確使用API並不能阻止異常條件的發生。第二,一旦產生了異常,使用API的程序員可以採取有用的動作對程序進行處理。

不要忽略異常

有些程序員會寫出下面的代碼

try {
...
} catch (SomeException e) {
}

  空的catch塊會使異常達不到應有的目的,異常的目的是強迫你處理不正常的條件。忽略一個異常,就如同忽略一個火警信號一樣 -- 若把火警信號器關閉了,那麼當真正的火災發生時,就沒有人看到火警信號了。所以,至少catch塊應該包含一條說明,用來解釋為什麼忽略這個異常是合適的。

所以,不是不建議使用try catch,而是如何能正確的使用try catch


除了允許讓框架來try-catch用log記錄異常,不建議用戶使用try-catch。

一個靠try-catch意淫出來的「穩定」系統,必然是充滿了不定性。


推薦閱讀:

如果BUG和電腦病毒入侵到現實世界裡,TA們會長什麼樣?
如何正確高效的查找遊戲「卡頓」原因?
自學編程最大困難是什麼?

TAG:程序員 | 編程 | Bug | 代碼質量 | C# |