簡體   English   中英

為什么嘗試訪問null屬性會導致某些語言出現異常?

[英]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

如果foobarbaz任何一個為空,我的代碼就會被破壞。 :(此外,如果我在控制台中評估以下表達式,似乎有不一致的結果:

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.

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