2008-09-22 24 views
28

我確定我們都在某個時間或其他時間收到了奇妙含糊的「對象引用未設置爲對象實例」異常。確定問題的對象通常是設置斷點和檢查每個語句中的所有成員的乏味任務。在拋出NullReferenceException時檢測目標對象是什麼

有沒有人有任何技巧可以通過程序化的方式或其他方式輕鬆有效地識別導致異常的對象?

- 編輯

看來我很模糊,就像異常=)。重點是_不必調試應用程序來查找錯誤的對象。編譯器/運行時確實知道該對象已被分配/聲明,並且對象尚未實例化。有沒有一種方法來提取/確定在捕獲的異常

@ W·克雷格交易

你解釋這些細節,這是一個設計問題的結果可能是最好的答案,我可以得到的。我對防禦性編碼相當強迫,並且在修復了我的習慣之後,設法消除了大部分這些錯誤。其餘的只是調整我沒有結束,並導致我發佈這個問題到社區。

感謝大家的建議。

+0

我不明白 - 如果您將調試器設置爲在NullReferenceExceptions上中斷,當然你可以看到哪個變量在引發異常的時候導致了錯誤? – Grokys 2008-09-22 15:39:57

回答

16

在引發NRE的地方,沒有目標對象 - 這是異常的要點。您希望的最多的是捕獲發生異常的文件和行號。如果您在識別哪個對象引用導致問題時遇到問題,那麼您可能需要重新考慮您的編碼標準,因爲這聽起來像您在一行代碼中做得太多。

這種問題的更好的解決方案是Design by Contract,無論是通過內置的語言結構,還是通過庫。 DbC會建議預先檢查超出範圍數據的方法(即:Null)的任何傳入參數並拋出異常,因爲該方法不適用於錯誤的數據。

[編輯匹配問題編輯:]

我覺得NRE介紹誤導你。 CLR遇到的問題是,當對象引用爲Null時,系統要求它取消引用對象引用。就拿這個例子程序:

public class NullPointerExample { 
    public static void Main() 
    { 
    Object foo; 
    System.Console.WriteLine(foo.ToString()); 
    } 
} 

當你運行它,它會拋出一個NRE第5行,當它試圖評估對FOO的ToString()方法。沒有要調試的對象,只有未初始化的對象引用(foo)。有一個類和一個方法,但沒有對象。


回覆:克里斯Marasti - 喬格的answer

你不應該拋出NRE自己 - 這是一個系統異常具有特定意義:CLR(或JVM)試圖評估的對象引用沒有初始化。如果您預先檢查了一個對象引用,那麼請拋出某種無效的參數異常或特定於應用程序的異常,但不會引發NRE,因爲您只會混淆需要維護應用程序的下一位程序員。

+1

+1對於「在一行代碼上做得太多」,因爲我決定這是我的問題。 – AaronLS 2012-05-16 22:20:48

+0

非常有用的答案,謝謝 – Jack 2016-06-19 06:54:15

1

除了查看堆棧跟蹤外,實際上並沒有太多可以做的事情;如果您在同一行代碼中取消引用多個對象引用,則無法確定哪一個爲空而不設置斷點。你可以通過每行只取消一個對象來避免這種情況,但這會導致一些非常糟糕的代碼。

1

那麼,你真的不能識別物體,因爲它不存在,因此,你所得到的例外。

1

行#和文件通常都是你需要找到的罪魁禍首。如果您是拋出異常的人,請考慮使用ArgumentNullException(如果適用),或者檢查空值並拋出NullReferenceException,其中包含有關空字段的更多詳細信息。

編輯@您的編輯:)

據我所知,你就必須檢查堆棧跟蹤字符串以獲得該行#和文件。最好的辦法是獲得最內層的異常,然後查看堆棧跟蹤的第一行。如果你希望能夠通過編程的方式解析這些信息來找出哪個字段導致null,並且用該字段的名字做一些事情,我擔心你會失去運氣。

@W。 Craig Trader

好點。對於傳遞給方法的空值,應拋出ArgumentNullException。對於一個尚未初始化的成員變量,像InvalidStateException這樣的東西可能會很好拋出。不幸的是,我在MSDN中找不到任何這樣的異常。滾動你自己的?

+0

您可能會考慮`InvalidOperationException`,這是針對[[]]方法調用對於對象當前狀態無效的情況` – 2010-05-27 04:55:42

0

如果您正在捕捉友好用戶消息或日誌記錄的例外情況,則可能希望調試器在調試時停止發生異常。轉到Debug/Exceptions並檢查你希望調試器停止運行的異常類型,在你的情況下,System.NullReferenceException。

0

設置VS打破異常,然後當你得到你的錯誤,它通常很明顯是什麼線。堆棧跟蹤窗口會告訴你如何到達那裏。除此之外,你可以做的不多。

0

僅供參考,類似的線程:Should I catch exceptions only to log them?

要點是要有效地捕捉異常。根據我的經驗,目標是確保程序員檢查代碼中的空引用 - 但我們知道實際上我們錯過了一些。 UI代碼應該有一定程度的異常處理。我喜歡我對這個問題的回答:My Answer。更重要的是,1800 information,誰指出,你只是拋出,而不是拋出前爲了捕捉整個堆棧跟蹤這是你如何最終調試這些問題的評論。

14

正如一些答案指出的,告訴Visual Studio在拋出NullReferenceException時中斷。

如何告訴VS打破時未處理的異常拋出

  • 調試菜單|異常(或按Ctrl + Alt鍵+Ë
  • 鑽入通用語言運行時異常
  • 鑽入系統
  • 查找System.NullRefernceException,並檢查框打破每當這個異常被拋出,而不是允許它繼續執行任何Catch塊。

所以現在,當它發生時,VS會立即中斷,Current Statement行將被放在表達式中評估爲null。

這個設施是所有類型的異常,包括自定義的有用的(可以添加完全限定的類型名稱,和VS將調試時間匹配它)

的一個缺點這種方式是否有碼加載到調試器中,該調試器遵循拋出的錯誤做法並捕獲許多您正在尋找的異常,在這種情況下,它會變成草垛/針頭問題(除非您可以修復該代碼 - 然後您已經解決了兩個問題:)


另一個可能派上用場的竅門(但只限於某些語言)是使用o F中的當(或等同物)關鍵字...在VB中,這看起來像

Try 
    ' // Do some work   ' 
Catch ex As Exception When CallMethodToInspectException(ex) 

End Try 

這裏的訣竅在於,當表達式求值之前調用堆棧被退繞至Catch塊。因此,如果您使用調試器,則可以設置表達式的斷點,並且如果您查看callstack窗口(Debug | Windows | Callstack),則可以看到並導航到觸發異常的行。 (您可以選擇從CallMethodToInspectException返回false,因此Catch塊將被忽略,並且運行時將繼續通過堆棧搜索相應的Catch塊 - 這可以允許進行不會影響行爲的日誌記錄,並與超過漁獲物和再次引發開銷更少)


如果你在非交互記錄只是感興趣,那麼假設你有一個調試版本(或在一定程度上,你必須做處理優化問題,使用PDB發佈構建),您可以獲得大部分從Exception ToString跟蹤錯誤所需的信息,包括堆棧跟蹤與行號。

但是,如果行號不夠,您可以通過爲異常提取StackTrace(使用上述技術或僅使用上面的技術)來獲得列號(非常多,特定的本地或表達式爲null)在catch塊本身):

int colNumber = new System.Diagnostics.StackTrace(ex, true).GetFrame(0).GetFileColumnNumber(); 

雖然我還沒有看到它做什麼的NullReference或其他運行時產生的異常,還可能有興趣在Exception Hunter看作爲一個靜態分析工具。

0

關於設置Visual Studio捕捉異常(建議here),請不要在解決問題後刪除此選項。我剛剛浪費了半個小時,試圖弄清楚爲什麼我的應用程序在System.Windows.Forms的某個部分深處掛着......

相關問題