[英]Why does trying to access a property of null cause an exception in some languages?
對於某些編程語言(例如C#,Javascript)而言,最讓我困擾的是,嘗試訪問null
屬性會導致出現錯誤或異常。
例如,在以下代碼段中,
foo = bar.baz;
如果bar為null
,C#將拋出一個令人討厭的NullReferenceException
,我的Javascript解釋器將抱怨Unable to get value of the property 'baz': object is null or undefined
。
從理論上講,我可以理解這一點,但在實際代碼中我常常有一些深層對象,比如
foo.bar.baz.qux
如果foo
, bar
或baz
任何一個為空,我的代碼就會被破壞。 :(此外,如果我在控制台中評估以下表達式,似乎有不一致的結果:
true.toString() //evaluates to "true"
false.toString() //evaluates to "false"
null.toString() //should evaluate to "null", but interpreter spits in your face instead
我絕對鄙視編寫代碼來處理這個問題,因為它總是冗長,臭的代碼。 以下不是人為的例子,我從我的一個項目中抓取了這些(第一個是Javascript,第二個是C#):
if (!(solvedPuzzles &&
solvedPuzzles[difficulty] &&
solvedPuzzles[difficulty][index])) {
return undefined;
}
return solvedPuzzles[difficulty][index].star
和
if (context != null &&
context.Request != null &&
context.Request.Cookies != null &&
context.Request.Cookies["SessionID"] != null)
{
SessionID = context.Request.Cookies["SessionID"].Value;
}
else
{
SessionID = null;
}
如果整個表達式返回null
如果任何一個屬性為null,那么事情會變得容易得多。 上面的代碼示例可以簡單得多:
return solvedPuzzles[difficulty][index].star;
//Will return null if solvedPuzzles, difficulty, index, or star is null.
SessionID = context.Request.Cookies["SessionID"].Value;
//SessionID will be null if the context, Request, Cookies,
//Cookies["SessionID"], or Value is null.
有什么我想念的嗎? 為什么這些語言不會使用此行為 ? 由於某種原因難以實施嗎? 它會導致我忽視的問題嗎?
它會導致我忽視的問題嗎?
是的 - 它會導致您希望存在非空值的問題,但由於存在錯誤,您將獲得空值。 在那種情況下, 你想要一個例外 。 靜默失敗並堅持使用糟糕的數據是一個非常糟糕的主意。
某些語言(例如Groovy)提供了一個可以幫助的空安全解除引用運算符。 在C#中它可能看起來像:
SessionID = context?.Request?.Cookies?["SessionID"]?.Value;
我相信C#團隊過去曾考慮過這個問題並發現它有問題,但這並不意味着他們將來不會重新考慮它。
然后,如果尾部是調用返回bool的方法,那么呢? Main.Child.Grandchild.IsEdit()
和bool IsEdit();
我認為最好有一個返回默認行為的“null”實例(這稱為空對象模式 )。
這樣,對於期望null表示問題的其他人,他們會得到他們的異常,但是如果你知道默認對象是可以接受的,你可以實現它而不用擔心它。 然后解決這兩種情況。
您可以從INull派生所有“null”對象,將它們放入與其類名稱相關的哈希值,然后在成員為空時引用它。 然后你可以控制默認的“null”實現(如果對象是“null”則可以接受)
此外,“null”對象可以在訪問異常時拋出異常,這樣您就知道您訪問了異常,並選擇何時可以。 因此,您可以實現自己的“空對象異常”。
如果他們為此提供任何語言支持,它可以是每個人,全面的翻譯單元,也可以是具有限定符的對象(最后一個是更好的選項),此時它不會是默認的,你可能已經實現了一個默認的“null”實例。
我可能會建議,如果你必須檢查那么多空值,那么你的代碼結構很差。 顯然,由於我沒有你的代碼,我不能說得最終。 我建議你將代碼分成更小的函數。 這樣,您的代碼只需要一次檢查一個或兩個空值,並且可以更好地控制在任何時候某些內容為空時會發生什么。
一般來說foo.Baz的意思是“在實例上如果foo,執行baz成員”。 這可能是一種動態語言,虛擬成員或任何東西 - 但我們在前提的第一步失敗了:沒有實例。
您的邊緣是一個空安全的成員訪問運算符。 非常罕見,坦率地說,我不同意你聲稱這會產生臭臭的代碼(雖然我可以說你在一個表達式中查詢太多級別以保持健康)。
同樣,我不同意null.toString()
應該返回“null” - IMO這是完全正常的失敗。 將對null的訪問視為“正常”本質上是錯誤的IMO。
有什么我想念的嗎? 為什么這些語言不會使用此行為? 由於某種原因難以實施嗎? 它會導致我忽視的問題嗎?
當一個變量為null
時,如果沒有預期,你提到的語言會選擇提前失敗 。 這是創建正確工作程序時的核心概念。
選項是隱藏空指針問題,程序會再運行一行,但可能會崩潰甚至更糟,導致輸出錯誤!
此外,當嘗試修復錯誤時,如果它立即拋出NullReferenceException
並且調試器向您顯示開始查看的行, NullReferenceException
容易修復它。 替代方案是模糊的症狀 - 更難調試!
當你不應該使用null
編程是很常見的。 在您的上述考試中, null
意味着什么。 答案是,它沒有任何意義。 作為代碼的讀者,我會假設作者忘了一些東西。
通常最好的解決方案是引入一個小的,我稱之為“null對象”,可以返回而不是null
。 在您的示例中,可能會返回“NoDifficulty”或“UnknownDifficulty”類以產生最終結果 - 干凈的代碼。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.