2009-02-23 44 views
11

在我看來,我的很多調試時間花在追查複雜語句中的空引用異常上。例如:爲什麼空引用異常不能指定具有空引用的對象?

For Each game As IHomeGame in _GamesToOpen.GetIterator() 

爲什麼,當我得到一個NullReferenceException,我可以得到的堆棧跟蹤的行號,但不是等於null的對象的名稱。換句話說,爲什麼:

Object reference not set to an instance of an object. 

,而不是

_GamesToOpen is not set to an instance of an object. 

Anonymous object returned by _GamesToOpen.GetIterator() is null. 

game was set to null. 

這是嚴格意義上的設計選擇,旨在保護匿名的代碼或有沒有一個比較編譯器設計中的消極原因是不將此信息包含在調試時異常中?

回答

11

異常是運行時的東西,變量是編譯時的東西。

事實上,在你的例子中的變量是表達式。表達式並不總是簡單的變量。在運行時,表達式將被評估,並且方法將在結果對象上被調用。如果該表達式的值是null,則運行時將拋出NullReferenceException。假定:

Dim a as New MyObject 
Dim b as String = MyObject.GetNullValue().ToString() 

什麼錯誤消息應該運行的回報,如果GetNullValue()方法返回null

+2

行號也是一個運行時間的事情。調試時編譯包含各種編譯時間事件(類和方法名稱,行號等)爲什麼不包含變量名稱? – 2009-02-23 18:36:03

+1

IL級別實際上存在類和方法和參數名稱。但是生成的IL中變量幾乎沒有了。基本上,沒有具體的方法將異常與特定變量聯繫起來:假設「if(a 2009-02-23 18:44:42

+0

由於上面的評論而被接受。 – 2009-02-23 19:01:11

1

一個簡單的方法來捕獲此調試,以在使用 對象之前放置Assert語句,檢查null並輸出有意義的消息。

1

在發佈版本中,變量名稱從符號中剝離出來,代碼甚至可能被優化爲不具有變量的特定內存位置,但只保留其中一個寄存器的引用(取決於範圍的可變用法)。因此,可能無法從參考位置中扣除變量的名稱。

在調試版本中,有更多關於變量的信息。但是,無論構建風格如何,異常對象都需要以相同的方式工作。因此,它會根據任何口味可以訪問的最小信息來執行操作。

1

幾件事情......

1)當你使自己的異常記住這一點(如果你是惱火它,它爲這個別人會,如果你對別的東西做的是在你煩惱)。鑑於異常路徑根本不應該是典型的路徑,所以花在生成異常上的時間有用信息是非常值得的。

2)作爲一個通用的編程實踐的研究採用這種風格,你將有少得多的問題(是你的代碼將不再在線路方面,但你會節省很多時間):

一)從來沒有做ab()。c(); do x = a.b(); X。C(); (在單獨的行上),你可以看到一個空值,或者a.b()的返回值爲null。

b)永遠不會將方法調用的返回作爲參數傳遞 - 總是傳遞變量。一個(FOO());應該是x = foo();斧頭);這一個是更多的調試和能夠看到的價值。

我不知道爲什麼像.net和Java這樣的環境沒有提供運行時版本,它確實有更多關於這些類型的異常的信息,比如索引在數組越界上,名稱當它是空的變量,等等的

2

對於像Java語言被編譯成獲得由VM解釋字節碼,假設你有一個類X與現場x,它的價值是null了一定的參考。如果你寫

x.foo() 

字節碼可能是這樣的:

push Xref   >> top of stack is ref to instance of X with X.x = null 
getField x   >> pops Xref, pushes 'null' on the stack 
invokeMethod foo >> pops 'null' -> runtime exception 

的一點是,該堆棧上需要一個非空引用的操作就可以在例如操作,如invokeMethod中,不能也不知道空引用來自哪裏。