2008-10-08 69 views
28

我的理解是,普通的智慧說只使用異常來實現真正的特殊條件(事實上,我幾次在SO中看到過這種說法)。真的是異常的例外錯誤?

然而,克齊斯茨托夫·克瓦林納說:

一個關於異常的最大的誤解是,他們對現實情況是,他們傳達錯誤條件「​​得天獨厚的條件。」從框架設計的角度來看,沒有像「特殊條件」那樣的東西。一個條件是否例外取決於使用的上下文,但可重用的庫很少知道它們將如何使用。例如,對於簡單的數據輸入應用程序,OutOfMemoryException可能是例外;對於自己進行內存管理的應用程序(例如SQL服務器)來說並不是那麼例外。換句話說,一個人的特殊狀況是另一個人的慢性病。

隨後,他還接着說,異常應該用於:

  • 用法錯誤
  • 程序錯誤
  • 系統故障

考慮克齊斯茨托夫·克瓦林納是在MS的CLR團隊的PM我問:你怎麼看待他的陳述?

回答

21

這聽起來過於簡單,但我認爲在適當的地方簡單地使用例外是有意義的。在像Java和Python這樣的語言中,異常很常見,特別是在某些情況下。例外適用於您想要通過代碼路徑冒泡的錯誤類型,並強制開發人員明確捕獲。在我自己的編碼中,當錯誤不能被忽略時,我認爲是正確的時候添加異常,或者拋出異常而不是將錯誤值返回給函數調用等更優雅。

某些爲此,我能想到的副手的例外是最合適的地方:

  • NotImplementedException - 指定一個特定的 方法或函數的很恰當的方式是不可用的,而不是簡單地返回而不做任何 。
  • 內存溢出例外 - 這是很難想象的處理這種 類型的錯誤,更好的辦法,因爲它代表了一個進程範圍或操作系統的整體內存分配失敗 。當然這是必不可少的處理!
  • 的NullPointerException - 訪問一個空變量是一個程序員錯誤,IMO 這是另一個良好的地方強制的錯誤,以氣泡的表面
  • ArrayIndexException - 在像C無情語言,緩衝區溢出 是災難性的。更好的語言可能會返回某種類型的空值,或者在某些實現中返回空值,甚至可能會環繞數組。在我看來,拋出一個 異常是一個更優雅的迴應。

這絕不是一個完整的清單,但希望能夠說明這一點。在優雅和合乎邏輯的地方使用例外。像編程一樣,正確的工作是正確的工作是好建議。毫無疑問,瘋狂是毫無意義的,但完全忽略一個強大而優雅的工具同樣是不明智的。

11

對於編寫框架的人來說,這可能很有趣。

對於我們其他人來說,這是令人困惑的(並且可能無用)。對於普通的應用程序,必須將例外作爲「特殊」情況。異常會中斷程序的普通順序演示。

你應該謹慎對待打破你的程序普通的從上到下的順序處理。異常處理 - 故意 - 難以閱讀。因此,爲標準情景之外的事物保留例外情況。

示例:不要使用異常來驗證用戶輸入。人們總是在輸入錯誤。這不是特例,這就是我們編寫軟件的原因。這就是if語句的用意。

當您的應用程序發生OutOfMemory異常時,捕獲它沒有意義。那很特別。 「順序執行」假設在窗口之外。你的應用程序註定要崩潰,並希望在你崩潰之前完成RDBMS事務。

+2

嘿,我不是故意粗魯,但你不會以任何理由支持你的陳述。 – 2008-10-08 00:49:47

+0

這是正確的,對於框架或系統開發,異常處理與應用程序完全不同。 – OscarRyz 2008-10-08 00:56:15

+0

@Esteban Araya:好點 - 我沒有提到從頭到尾的連續文本很重要。 – 2008-10-08 00:59:53

1

我一直在想這個。 「特殊」是什麼意思?也許沒有嚴格的定義,但是在給定的背景下,我們可以使用哪些經驗法則來決定什麼是特殊的?

例如,如果說「例外」條件是違反函數合約的條件是否公平?

0

KCwalina有一個觀點。 這將是很好的代碼將失敗(達到極限)

我同意S.Lott有時驗證是比拋出異常更好。 話雖如此,OutOfMemory並不是您在應用程序中所期望的(除非它正在分配大內存&需要內存才能繼續)。

我認爲,這取決於應用程序的領域。

6

由於我通常使用Python進行編程,而且在那種語言中,異常無處不在,對我而言,異常可能代表從系統錯誤到完全合法的狀況。

例如,檢查字符串是否包含整數的「pythonic」方法是嘗試int(theString)並查看它是否引發異常。這是一個「特殊錯誤」嗎?

同樣,在Python中,for循環始終被認爲是作用於迭代器的,並且迭代器在完成作業(for循環捕獲該異常)時必須引發'StopIteration'異常。無論如何,這是「特殊」嗎?

3

我認爲越接近實際,你越不適合作爲錯誤交流的手段。在諸如Java或.net之類的更高層抽象中,例外情況可能會將錯誤消息傳遞給調用者。但是,這在C中並不是這樣。這也是一個框架vs api設計決策。

8

確實很難知道究竟是什麼構造了一個「例外條件」,以保證在程序中使用例外。

一個實例對於使用來傳達錯誤原因非常有幫助。從克齊斯茨托夫·克瓦林納報價提到:「特殊條件」

其中一個最大的誤解 約例外的是,他們對 現實 是,他們傳達 錯誤條件。

舉一個具體的例子,比如說我們有一個getHeader(File f)方法是閱讀從一個文件中的一些標題和返回FileHeader對象。

嘗試從磁盤讀取數據可能會出現幾個問題。也許指定的文件不存在,文件包含無法讀取的數據,意外的磁盤訪問錯誤,內存不足等等。具有多種失敗手段意味着應該有多種方式來報告出錯的地方。

如果沒有使用異常,但需要傳達發生的錯誤類型與當前的方法簽名,我們可以做的最好的方法是返回null。由於獲得null不是很豐富,我們從這個結果得到的最好的溝通是「發生了某種錯誤,所以我們不能繼續,對不起。」 - 它不傳達錯誤的原因。

(或者,我們可以有這表明FileNotFound的條件等,仿真錯誤代碼FileHeader裏的對象類的常量,但事實真是惡臭有一個boolean類型與TRUE, FALSE, FILE_NOT_FOUND的。)

如果我們已經得到了一個FileNotFoundDeviceNotReady異常(假設),至少我們知道錯誤的來源是什麼,如果這是一個最終用戶應用程序,我們可以通過解決該問題的方式來處理錯誤。

使用異常機制提供了一種通信方式,不需要回退就可以使用錯誤代碼來通知不在正常執行流程中的條件。

但是,這並不意味着一切都應該由異常來處理。正如S.Lott指出:

不要使用異常來驗證用戶輸入 ,例如。人們總是犯 錯誤。這就是if語句的用途。

這是一件無法強調的事情。不知道何時使用例外的危險之一是傾向於異常開心;在輸入驗證足夠的情況下使用異常。

當定義和拋出異常時,確實沒有意義,因爲在這種情況下需要處理的只是通知用戶輸入的內容。

另外,應該注意的是,用戶輸入是預期在某個點有錯誤的輸入。在將來自外部世界的輸入交給程序的內部之前,這是一種防禦措施,用於驗證輸入。

要確定哪些是特殊的,哪些不是特別的,有點難。

1

我認爲有幾個很好的理由爲什麼應該使用異常來捕捉意外問題。

首先,它們創建一個對象來封裝異常,根據定義,它必須比處理簡單的if語句花費更多的代價。作爲一個Java示例,您應該調用File.exists(),而不是經常期待和處理FileNotFoundException。其次,在當前方法之外捕捉到的異常(或者甚至是類)使得代碼難以閱讀,這比在一種方法中處理完全存在的代碼要困難得多。

話雖如此,我個人例外。它們讓你明確地處理所有那些可能發生但可能永不會錯誤的類型錯誤,這會導致你重複編寫print-an-error-and-abort-on-non-zero-return-每個方法調用的代碼處理。

我的底線是......如果您可以合理地期望它發生,那麼它是您的應用程序的一部分,您應該編寫它。其他任何事情都是例外。

0

Krzysztof Cwalina的聲明有點誤導人。原來的聲明提到'特殊條件',對我來說,我是一個自然而然的人,他定義了什麼是特殊的。不過,我認爲這個信息通過了OK,因爲我認爲我們都在討論'開發者'例外情況。

例外情況非常適合溝通,但採用小小的層次結構設計,它們對於某些問題的分離,特別是層與層之間(DAO,Business等)也非常有用。當然,這隻有在你以不同的方式處理這些異常時纔有用。

層次結構的一個很好的例子是spring的數據訪問異常層次結構。

3

如果您練習「說,不要問」,那麼例外就是程序說「我不能這樣做」的方式。這是「例外」,因爲你說「做X」,它不能做X.一個簡單的錯誤處理情況。在某些語言中,這種方式很常見,在Java和C++中,人們有其他意見,因爲異常變得非常昂貴。

一般:除了剛纔的意思是「我不能」

務實:...如果你能負擔得起的工作在你的語言的方式。

公民身份:...和您的團隊允許。

0

我認爲他是對的。看看java中的數字解析。你甚至不能在解析之前檢查輸入字符串。如果出現問題,您必須解析並檢索NFE。解析失敗是例外嗎?我想不是。

0

我當然認爲只有在有特殊情況時才應該使用異常。

麻煩在於「例外」的定義。這裏是我的:

如果條件超出假定的正常 引發異常的系統部分的行爲,則條件是例外。

這有一些影響:

  • 特殊取決於你的假設。如果函數假定它傳遞了有效的參數,那麼拋出一個IllegalArgumentException是可以的。然而,如果一個函數的合約說它會以某種方式糾正輸入中的輸入錯誤,那麼這個用法是「正常的」,它不應該在輸入錯誤時拋出異常。
  • 例外取決於子系統分層。網絡IO功能當然可能引發異常,因爲它假定有效的連接。但是,基於ESB的消息代理可能會處理丟棄的連接,所以如果它在內部使用這樣的網絡IO函數,那麼它需要適當地捕獲並處理錯誤。如果不是很明顯,try/catch實際上等價於一個子系統,「我的某個組件的某個特殊條件實際上被認爲是正常的,所以我需要處理它」。
0

在「有效的Java第二版」中使用異常應該用於例外情況的說法:最好的Java書籍之一。

麻煩的是,這是取消了上下文。當作者聲明異常應該是例外的時候,他剛剛展示了一個使用異常來終止while循環的例子 - 一個不好的異常使用。引用:

正如其名稱所示, 僅用於特殊情況;他們不應該被用於控制流程的普通 。

所以這一切都取決於你的「例外情況」的定義。從上下文中脫離出來,你可以暗示它應該很少被使用。

使用異常來代替返回的錯誤代碼是很好的,而使用它們來實現「聰明」或「更快」的技術並不好。這通常意味着「特殊條件」。

檢查異常 - 小錯誤不是bug,也不應該暫停執行。恩。 IO或文件解析 未經檢查的異常 - 編程「錯誤」,違反方法合同 - 例如。 OutOfBoundsException。或者導致繼續執行一個非常糟糕的想法的錯誤 - 例如IO或文件解析非常重要的文件。也許是一個配置文件。

0

什麼是需要什麼工具來完成這項工作。

例外是一個非常強大的工具。在使用它們之前,問問你是否需要這種力量以及隨之而來的複雜性。

例外可能會出現簡單,因爲你知道,當有異常的行被擊中一切都停了下來。儘管這裏發生了什麼?

會發生未捕獲的異常嗎?

全局錯誤處理是否會捕獲異常?

異常是否會被更多嵌套和詳細的錯誤處理處理?

您必須知道堆棧中的所有內容才能知道該異常會執行哪些操作。這違反了獨立的概念。現在這個方法依賴於錯誤處理來做你期望的事情。

如果我有一個方法,我不應該在乎那種方法以外的東西。我應該只關心輸入是什麼,如何處理它,以及如何返回響應。

當你使用你基本上是說,我不在乎在這裏發生了什麼異常,出了問題,我不希望它變得更糟,做任何需要做的事情,以減輕這個問題。

現在,如果你關心如何處理錯誤,你會做一些更多的思考和構建到方法的接口,例如如果您試圖找到某個對象,則可能會返回該對象的默認值(如果找不到該對象),而不是拋出諸如「找不到對象」之類的異常。

當你建立自己的錯誤處理到你的方法接口,不僅是方法的簽名更具描述性的它可以做什麼,但它把如何處理的方法的調用者錯誤的責任。調用者方法可能能夠解決它,如果沒有,它會再次上報鏈。最終你會到達應用程序的入口點。現在應該拋出一個異常,因爲如果你正在使用應用程序的公共接口,你應該更好地理解如何處理異常。

讓我給你一個我的Web服務錯誤處理的例子。

Level 1. Global.asax中的全局錯誤處理 - 這是防止未捕獲的異常的安全網。這絕不應該故意達成。

2級Web服務方法 - 在一個try/catch包裹,以保證它會一直遵守其JSON接口。

級別3.工作者方法 - 這些方法獲取數據,處理數據並將其原始返回到Web服務方法。

在worker方法中拋出異常是不對的。是的,我有嵌套的Web服務方法錯誤處理,但該方法可以在其他可能不存在的地方使用。

相反,如果一個工人的方法來獲取記錄和記錄不能被發現,它只是返回null。 Web服務方法檢查響應,當它發現null時,它知道它不能繼續。 Web服務方法知道它有錯誤處理來返回json,所以拋出異常只會返回json中發生的細節。從客戶的角度來看,將它打包成可以輕鬆解析的json是非常棒的。

你看到每一塊只是知道它需要做什麼,做它。當您在混音中拋出異常時,會劫持應用程序流。這不僅導致難以遵循代碼,而且濫用異常的反應是try/catch。現在你更可能會濫用另一個非常強大的工具。

我經常看到try/catch捕捉應用程序中的所有內容,因爲開發人員害怕他們使用的方法比看起來更復雜。

0

下面是異常的定義:異常是一個事件,它發生在程序執行過程中,會中斷程序指令的正常流程。

因此,要回答你的問題,不。例外情況是中斷事件,其可能是也可能不是例外。我喜歡這個定義,它很簡單,並且每次都有效 - 如果你像我那樣購買例外。例如,用戶提交了一個不正確的un/pw,或者你有一個非法的參數/壞的用戶輸入。在這裏拋出一個異常是解決這些問題最直接的方式,這些問題具有破壞性,但並不是特殊的,甚至是無法預料的。

他們可能應該被稱爲中斷,但那艘船已經駛了。