C#將引入可空的引用型別

weixin_33763244發表於2017-04-20

是的,標題沒錯。C#其中一份新提案假定,所有的引用型別在預設情況下都是不可空的。在新語法下,你需要顯式地標明一個引用變數是可空的,就像對值型別所做的那樣。

\\

和值型別一樣,T是指不可空型別,而T?是指可空型別。以下情況會產生警告:

\\
  • 取消對可空變數的引用;\\t
  • 一個可空變數或引數被賦值給一個不可空變數;\\t
  • 從T?[]轉換到T[];\\t
  • 從T[]轉換到T?[];\\t
  • 將一個空字面量賦值給一個不可空變數或引數;\\t
  • 建構函式沒有給所有的不可空欄位賦值;\

對於前兩種情況,如果你使用了感嘆號運算子(x!)或者編譯器能夠證明已經執行了null檢查,那麼警告將被忽略。

\\

實現細節

\\

底層編譯器將會忽略可空註解,因此,那不是問題。不過,在程式集級,應該有某種標記,可以說明庫在編譯時啟用了可空註解。

\\

由於所有這類可空的東西從技術上講都是一種破壞性修改,所以當前的計劃是允許開發人員選擇下面的類別:

\\
  • 可空警告;\\t
  • 不可空警告;\\t
  • 警告來自其他檔案中的註解。\

提案繼續寫道:

\\
\

選擇參與的粒度表明,這是一個類似分析器的模型,大段的程式碼通過編譯指令選擇參與和退出,使用者可以選擇安全級別。此外,每個庫的選項(“在準備好應對後果之前,忽略JSON.NET中的註解”)可以表示為程式碼中的屬性。

\
\\

根據預期,這種設計要達到以下三個目的:

\\
  • 使用者可以像他們希望的那樣逐步採用可空屬性檢查;\\t
  • 庫作者可以新增可空屬性註解,而不必擔心破壞使用者的程式碼;\\t
  • 除此之外,沒有“配置噩夢”之感。\

對於同一個方法,你不必進行可空和不可空的過載。雖然從技術上講,CLR支援這樣做,但那不是CLS或者通用語言規範的組成部分。這意味著,大多數編譯器都會不知道發生了什麼。HaloFour作了如下說明:

\\
\

modreq不是CLS。modopt確實支援過載,但需要具體瞭解所有重要編譯器的這個部分,因為至少要將修飾符複製到呼叫簽名裡。兩者都會破壞與現有方法簽名的相容。對於希望在整個BCL快速傳播的東西來說,使用modopt會成為巨大的障礙。

\
\\

泛型

\\

在使用泛型時,以下情況會出現額外的警告:

\\
  • 從C\u0026lt;T\u0026gt;轉換到C\u0026lt;T?\u0026gt;,除非型別引數是協變數(出);\\t
  • 從C\u0026lt;T?\u0026gt;轉換到C\u0026lt;T\u0026gt;,除非型別引數是反變數(入);\\t
  • 使用C\u0026lt;T?\u0026gt;,然後將型別引數限制為不可為空。\

使用“class”,則泛型強制非空。使用“class?”則允許空值。該提案繼續寫道:

\\
\

如果一個型別引數沒有約束,或者只有可空約束,則情況會稍微複雜一些:這意味著,相應的型別引數既可以為空,也可以不為空。在那種情況下,安全的做法是將型別引數既作為可空引數來處理,又作為不可空引數來處理,任何一個不滿足,就發出警告。

\
\\

陣列

\\

陣列是一項特殊的挑戰,因為在一個不可為空的陣列中,不一定可以確保每個槽都有一個值。

\\
\

對於一個非空引用陣列,我們無法通過充分地跟蹤來保證陣列的所有元素都被初始化。不過,在從陣列讀取資料或者傳遞陣列之前,如果新建立的陣列沒有元素被賦值,我們就會發出警告。那應該可以處理常見的情況,而又不帶來太多干擾。

\
\\

開放性設計問題

\\

使用default(T)應該發出警告嗎?還是說假定它會返回T?,而不是T?

\\

可以刪除區域性變數上的?而根據使用情況推斷其可空性嗎?

\\

引數可以使用T! x模式自動生成null檢查嗎?

\\

可以調整一下可空值型別,以便讓開發人員可以使用x.method代替x.Value.method嗎(這用在當x已知非空時,比如已經成功完成了null檢查)?

\\

更多資訊

\\

檢視英文原文:C# Futures: Nullable Reference Types

相關文章