簡體   English   中英

Debug.Assert與異常

[英]Debug.Assert vs Exceptions

出乎意料的是,我只能在SO上找到關於此主題的先前一個問題,而我的方法是讓社區獲得“信任投票”(或沒有!)。

因此,我的看法是:

  • 使用Debug.Assert聲明您所期望的事情是正確的。 當我們完全控制環境時,例如在驗證某些前提條件和后置條件的方法中,將使用此方法。
  • 在特殊情況下使用Exception。 處理外部資源(例如文件,數據庫,網絡等)是理所當然的。 但...

在以下情況下,它會變得有點模糊。 請注意,這是一個僅供參考的示例!

假設我們有類MyClass,它具有一個公共屬性MyMode和一個方法GetSomeValueForCurrentMode() 將MyClass視為打算在庫中交付(內置發行版)以供其他開發人員使用的類。

我們希望此類的外部用戶可以更新MyMode。 現在, GetSomeValueForCurrentMode()具有以下邏輯:

switch(MyMode)
{
case Mode.ModeA:
return val1;
case Mode.ModeB:
return val2;
default:
//Uh-uh this should never happen

}

我在這里得到的是MyClass的用戶將其置於無效狀態。 那我們該怎么辦呢?

默認情況下,我們應該Debug.Assert還是throw new InvalidOperationException (或其他)?

有一項口頭禪說我們不應該信任班級的用戶。 如果我們選擇Debug.Assert並將MyClass構建為發行版本(從而刪除了Debug Asserts),則該類的用戶將不會獲得有用的信息,因為他們將其置於無效狀態。 但這與另一句口頭禪相反,另一句口頭禪說只有在完全無法控制的情況發生時才拋出異常。

我發現我對此進行了盤旋-那些似乎沒有明確的“正確”答案的編程辯論之一。 因此,讓我們將其付諸表決!

編輯:我在一個相關的SO問題中注意到了此響應( 按合同設計是使用斷言還是異常? ):

經驗法則是,當您嘗試捕獲自己的錯誤時應使用斷言,而在嘗試捕獲其他人的錯誤時應使用異常。 換句話說,每當您獲取系統外部的任何數據時,都應使用異常檢查公共API函數的前提條件。 您應該對系統內部的功能或數據使用斷言。

對我而言,這很有意義,並且可以與下面概述的“先斷后投”技術結合使用。

歡迎思想!

首先,MyClass有效,當然應該用MyClass的invariant表示。

其次,您說“我們希望MyMode將由此類的外部用戶更新”-當然,此模式的設置者應具有典型的按合同設計的形式(作為任何公共功能):

  void Setter(mode m)
  {
    // INVARIANT ASSERT (1)
    // PRECONDITION ASSERTS (uses "m") (2)

    // BODY (3)

    // POSTCONDITION ASSERTS (if any) (4)
    // INVARIANT ASSERT (5)
  }

在(5)中,您將因尖叫聲明斷言而失敗,即不滿足不變式。 但是在(2)中,您會更早失敗,因為傳遞的模式m無效。 這將向用戶發送明確的消息,從而解決您的問題。

並且請不要告訴我,模式字段是公共的,用戶在沒有任何控制的情況下進行更改。

編輯:關於斷言和釋放模式,另請參見:

我基本上同意您所提出的問題的結論:如果A虱的代碼檢測到A虱犯了一個錯誤,那么A ssert就是這種情況(除非性能另有規定,否則應在生產代碼中保留斷言)。 如果Alice的代碼檢測到E ve的代碼中有錯誤,則假設Elice和Eve處於錯誤跟蹤軟件的相對方面 ,這就是E Xceptions的情況。

現在,這是一般的經驗法則。 稍作修改的斷言也可以用作“提示,開發人員!” 機制(然后不應將它們稱為“ ASSERT”,而應稱為“ HEADS_UP”或類似名稱)。 如果您的公司開發了客戶端/服務器產品,並且服務器將無效數據發送給客戶端該怎么辦? 如果您是客戶程序員,您會覺得要將其視為外部數據(Eve的數據是kaputt),並且想引發異常。 但是,一個“更軟”的斷言可以使Visual Studio的調試器立即停止工作,這是一種非常好方法,可以盡早發現這些問題並將其報告給服務器團隊。 在真正的安裝中,很可能是Eve和Alice之間的數據對Mallory進行了調整,但是大多數情況下,這實際上是您的一位同事的錯誤,並且您希望在發生時看到它-這就是為什么我稱呼他們“平視”斷言:它們不能代替異常,但可以給您警告和檢查問題的機會。

我同意這里的大多數人的意見,並遵循按合同設計。 您應該嘗試非常清楚地區分已部署代碼(合同)中的需求和設計期間的預期狀態(調試斷言)。

您應該始終將合同聲明作為例外(因為它們應該總是例外)。 大多數框架都內置了用於捕獲調試斷言的機制。 但是在運行時,您應該始終拋出異常。

我使用自定義庫來幫助解決此問題(在C#/ VB.NET中)。 如果您對這在實際中的工作方式感興趣,我最近將其放在Codeplex( http://www.contractdriven.com/ )上。

這樣做的一個附帶好處是,隨着您開始更經常地使用DbC,您幾乎不需要使用調試聲明,因為已經在代碼中寫入了明確的保證,因此實際上很難進入無效狀態。

因此,您原始帖子中的問題...“我在這里要說的是MyClass的用戶將其置於無效狀態。那么我們應該怎么做?” ...永遠不會出現。

您可能再也不需要調試任何東西! ;-)

通常兩者:斷言,然后拋出。

您之所以斷言,是因為您想在開發過程中將錯誤的假設通知開發人員。

之所以拋出該錯誤是因為,如果這在發行版本中發生,則需要確保系統在處於不良狀態時不會繼續處理。

系統所需的可靠性特征可能會影響此處的選擇,但我認為“先斷后投”通常是一種有用的策略。

我對斷言的使用遵循了合同設計的思想。 基本上,您斷言輸入參數,全局變量和其他狀態信息在您進入函數時是有效的,並且斷言返回值和狀態在您離開時也有效。 如果在開始時遇到失敗的斷言,則說明調用代碼中存在錯誤,如果在結束時得到了錯誤,則此代碼中存在錯誤。 這些是前提條件和后期條件。

switch語句中的斷言僅在檢查了輸入的前提條件后才真正有用。 在這種情況下,如果您在函數中間遇到不可接受的狀態,則說明函數或調用的函數失敗。 就個人而言,我在這里堅持一個斷言,因為這不是一個例外。 正如您所暗示的,異常與資源有關,而不是錯誤。 但是,您可以創建發布版本中存在的自定義斷言處理程序,並在失敗時引發異常,以使程序有機會返回到穩定狀態。

我會說:如果錯誤是在別人的代碼中,或者是在不同子系統的代碼中(無論您是否寫過),請使用例外。 如果來自外部源(例如文件)的數據中存在錯誤,也請使用異常。

有時,您不知道數據是否來自外部來源。 如果安全性很重要,並且您可能正在處理未經驗證正確的外部數據,請使用異常。 如果安全性不重要,那么我將使用性能作為決勝局:是否可以在緊密循環中執行此代碼? 如果是這樣,請考慮使用斷言,因為您將在Release版本中獲得更好的性能。 否則,請使用異常。

它取決於語言,是斷言的,如果您使用糖語法,則應使用它。 但是在Java中,斷言需要打開才能起作用,因此異常更好。 但是,最好總是具有特定的異常,因此此處應為IllegalStateException。

在.Net中,當您進行發行版本構建時,您的程序中將不包含Debug類方法,因此,如果此代碼將其用於生產,則將必須引發異常。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM