什麼時候使用異常

2022-05-17 21:38:36 字數 2800 閱讀 8018

先說個題外話: 在公司做了倆件事, 是我覺得很有意義的, 第一就是成立了乙個php郵件組, 第二就是成立了乙個hi群. 目前倆者都有超過500 phpers在裡面. 我一直認為, 構建乙個交流平台, 讓同學們能順暢, 簡單的溝通, 是營造積極的技術學習氛圍的基礎和前提. 讓每個人的問題不會成為別人的問題, 則是最直接的利益. (後記: 不少人都問郵件組位址, 實在不好意思, 這個郵件組是公司內部的郵件組, hi也是公司內部的. 謝謝)

昨天, 有同事在郵件組提了個問題:

php應該什麼時候使用 exception ? 它的效能如何?

這個問題也算是乙個久經爭論的經典問題了. 我談談我的個人看法.

異常與之對應的錯誤碼(或者狀態碼), 到底各自有什麼優點, 缺點, 我們應該怎麼使用呢?

首先來說, 異常機制是在錯誤碼機制之後才出現的, 那麼根據演化論, 異常自然是避免了錯誤碼機制的一些不足. 這些不足包括.

1. 錯誤資訊不豐富

函式, 只能有乙個返回值(當然, lua可以返回多個, 但其實也相當於在php中返回乙個陣列), 我們見過最多的函式說明就是: 成功時候返回***, 錯誤的時候返回false, 然而乙個函式出錯我原因可能有多種, 出錯的種類更有多種. 乙個簡單的false, 並不能把具體的錯誤資訊告訴呼叫者.

於是, 我們也就見過一些, 這樣的函式說明: 如果返回值大於0, 則表示成功的狀態碼, 如果返回值小於0, 則表示出錯的狀態碼.

然而, 這個要求函式是返回整形(或者數字), 對於一些其他函式, 我們並不能通過0, >0, <0來判別, 並且, 即使通過這樣的方式, 我們還需要用返回的錯誤碼和一些預定義巨集(或者呼叫類似strerror())來獲取具體的, 可讀的錯誤資訊.

於是, 就有一些函式使用全域性的錯誤碼, 和錯誤資訊, 來儲存具體的錯誤資訊, 這個時候我們就看到這樣的函式描述: 成功返回***, 出錯的時候返回false, 錯誤**儲存在全域性變數$errno中(至少大多數linux庫函式是這樣描述的, 呵呵).

okey, 這樣的方式確實可以工作, 但是, 是不是覺得, 很醜陋呢?

2. 加入錯誤狀態碼可能需要改變函式簽名

假設, 你編寫了乙個函式, 這個函式很簡單, 很簡單, 你認為他絕對不會出錯, 於是你申明為(用c語言為例, php沒有返回型別提示):

void dummy()

但是後來你慢慢修改了這個函式, 給了它更多的功能, 此時這個函式可能會失敗了. 而你現在根本無法為這個函式, 加入錯誤返回碼了.

也許有人說php沒有返回值型別限制一說, 但是想想php的建構函式, 建構函式是沒有返回值的, 當發生錯誤的時候, 如果你不使用異常, 我想你只能選擇die, 或者使用2中的方法來錯誤繼續執行了.

另外, 在乙個良好的軟體系統中, 返回型別其實也是約定俗成的, 當所有的使用的函式的地方, 都沒有檢查返回值的時候, 你還是無法為這個函式加入錯誤返回碼.

3. 錯誤狀態碼可能會被忽略

當你的乙個函式, 出錯了, 返回了錯誤狀態碼, 而呼叫方並沒有檢測這個返回值, 會發生什麼情況呢? -_#. 令一方面, 處處檢測返回狀態碼, 會造成**非常的,,ugly:

<?php

if (!call1())

if (call2() != success)

if (call3() < 0)

那麼現在我們來看看異常機制, 如果我們採用異常機制, 上面的**可以寫作:

<?php

try catch (exception $e)

更方便的, 如果你的**只是中間層, 你的呼叫方會負責處理錯誤的話, 你甚至可以簡單的寫作:

<?php

function myfunc()

而乙個異常物件, 可以包含更豐富的錯誤資訊, 比如錯誤資訊, 錯誤碼, 錯誤的行數, 檔案, 甚至出錯上下文, 等等, 避免的」1.錯誤資訊不豐富」的不足.

我們也可以為乙個返回void型別的函式增加異常, 而不改變他的函式簽名, 也就不會有上面說的」2.加入錯誤狀態碼可能需要改變函式簽名」. 對於php來說, 如果我們新加入的錯誤沒有**捉, 也不用擔心, 會明顯的出錯的. 也就不會發生上面所說的」3. 錯誤狀態碼可能會被忽略」的情況.

然而, 也有一些反對使用異常的聲音:

1. 效能

正如文章開頭提問中的: 「它的效能如何?」, 異常機制確實要比返回狀態碼的方式昂貴一些, 對於c++來說, 在異常發生的時候, 還要發生堆疊解退(對於php來說, 沒有這個邏輯, 具體的大家可以參看我之間寫的一篇文章:深入理解php原理之異常機制).

效能和方便, 往往是乙個矛盾體, 我只能說, 你需要權衡, 如果你寫的是乙個小的模組, 並且它的生命期可能很短, 也不需要什麼特殊的設計模式, 那我覺得你可以不用異常.

而如果你在為乙個龐大的軟體做開發, 我想你更應該看重的, 應該是, 它的可擴充套件性, 可維護性.

2. 太多可能的uncaught exception

如果, 你呼叫了乙個可能發生異常的函式, 但是卻沒有捕獲這個異常, okey, fatal error了, 所以讓我們的**看起來:

<?php

try catch ()

....

try catch ()

....

try catch ()

然而, 這個是可以經過良好設計避免的, 比如我在設計yaf的時候, 就提供了全域性異常處理, 也就是類似於, 你在最最頂層, 加上了乙個try catch, 所有的異常錯誤邏輯都加到這個裡面, 你也可以很方面的把你自己的異常加進去.

經常有人批評我是倆麵派, 呵呵, 但是在大家了解了上面的利弊以後, 是否也會和我一樣認為: 這個事情沒有定論呢? 一切從實際出發.

說了這麼多, 算是拋磚引玉, 歡迎補充

什麼時候使用引用 什麼時候使用指標

使用引用引數的主要原因有兩個 程式設計師能修改呼叫函式中的資料物件 通過傳遞引用而不是整個資料 物件,可以提高程式的執行速度一般的原則 對於使用引用的值而不做修改的函式 如果資料物件很小,如內建資料型別或者小型結構,則按照值傳遞 如果資料物件是陣列,則使用指標 唯一的選擇 並且指標宣告為指向cons...

什麼時候使用抽象類, 什麼時候使用介面

介面是一種協定,抽象類則相當於類模板。使用抽象類,而不要使用介面來分離協定與實現。如果需要提供多型層次結構的值型別,使用介面。如果乙個型別必須實現多個協定,或者協定適用於多種型別,使用介面。雖然抽象類和介面都支援將協定與實現分離開來,但介面不能指定以後版本中的新成員,而抽象類可以根據需要新增成員以支...

什麼時候使用介面,什麼時候使用抽象類

使用抽象類是為了 的復用,而使用介面的動機是為了實現多型性。抽象類適合用來定義某個領域的固有屬性,也就是本質,介面適合用來定義某個領域的擴充套件功能。一 什麼時候使用抽象類?當2個或多個類中有重複部分的時候,我們可以抽象出來乙個基類,如果希望這個基類不能被例項化,就可以把這個基類設計成抽象類。當需要...