最近VS2019正式版釋出了,裝下來順便試用了一下C#8.0,最大的看點應該就是可空取用型別了。不過C#8.0仍然處於Beta的狀態,而且試用時也遇到了幾個坑。
背景知識說明:
所謂的可空取用型別是指,一旦啟用了可空取用型別這個新特徵,取用型別將預設被視為不可空,無法賦予null,除非手工將它設為可空取用型別。
實戰示例:
首先是新建一個C#的專案,在 專案檔案(.csproj)裡加入兩行配置,目的是啟用“C#8.0語言”和“可空取用型別”:
<LangVersion>8.0LangVersion> <NullableContextOptions>enableNullableContextOptions>
整個檔案看起來是這樣的:
這樣就算是整個專案全域性啟用了可空取用型別了。
註意:
在VS2019正式版中,使用
<NullableContextOptions>enableNullableContextOptions>
而不是使用
<NullableReferenceTypes>trueNullableReferenceTypes>
後者在正式版中已經失效了。
如果不希望全域性啟用可空取用型別的話,可以在程式程式碼中加入以下編譯指令:
這樣可以在加入了該指令的檔案中,單獨啟用可空取用型別。但是,極度不推薦這種做法。為什麼呢?因為該指令僅僅在該檔案中有效,不能跨檔案生效,從而無法阻止null逃逸到使用了該指令的檔案中,也就是說,用了也等於沒用。
一個很簡單的例子足以證明:
註意上面專案檔案中並沒有全域性啟用可空取用型別,而下麵的Class1.cs中使用了編譯器指令來單獨啟用可空取用型別。
從執行結果可見,空取用仍然逃逸到使用了該指令的作用域中了。別說編譯錯誤,連編譯警告都沒有。完全達不到理想的效果。
因此,強烈建議在專案檔案中全域性啟用可空取用型別,而不是在某個源檔案中單獨使用。
另外,還有一點要註意的是,即使啟用了可空取用型別後,預設情況下,即使對不可空取用賦予null,編譯器也只會生成編譯警告,而不是編譯錯誤。仍然是能夠編譯透過的。一個大專案中,編譯警告不可避免,甚至可能會很多,從而淹沒了“給不可空取用型別賦予空值”這種不起眼的警告。
因此,建議將特定的警告視為錯誤。警告編號為8600、8625、8618、8604,或者將所有警告視為錯誤。具體是在專案檔案中加入以下設定(見圖一):
<WarningsAsErrors>8600 8625 8618 8604WarningsAsErrors>
或者在專案編輯器中設定也可以:
這是我自己測試得出的結果,可能還有其它潛在的相關警告編號我沒有測試出來。如果有誰知道的話,告訴我一下,謝謝。
做好這些配置之後,可以看到取用型別預設都不能賦予空值了:
這時候普通的取用型別的變數和引數都不能設為null了。
這樣可以防止空值擴散開來,引起惱人的空取用異常。
但是,這裡有個坑需要註意!!!!
struct裡不適用可空取用的規則!!
struct裡不適用可空取用的規則!!
struct裡不適用可空取用的規則!!
這種情況下直接執行,仍然會丟擲空取用異常!!!C#8.0仍未能完全封堵住空取用的逃逸。
其實我還是比較贊同用不可空取用型別的方案的,而不是可空取用型別的方案。畢竟我想要的,只不過是一個不可空的斷言,只是想利用不可空取用來劃分安全邊界,從而防止空值的擴散。簡單來說就是想將變數和引數明確宣告為不可空取用型別。因為歷史和現實的原因,大量的庫都還沒能全面使用可空取用型別。這種情況下,只有我使用可空取用型別,是不靠譜的。無法劃分安全邊界。
然而C#選擇了可空取用型別的方案,而且還不是強制啟用,而且預設只是警告。。跟沒有一樣。。。
比方說,我使用了一個第三方庫專案,而空值的來源是正好是該庫專案的,而我對這個庫並沒有原始碼或者修改許可權。這時候就無法阻止空值逃逸到我的專案中了。
還是之前的程式碼,只是稍微做一下修改。新增了一個庫專案ClassLibrary1,這個庫並沒有使用可空取用型別。
庫的程式碼如下:
很簡單,就是LibClass3.GetInstance()本應該傳回LibClass2的實體,但是出於某種原因,傳回了null。但是我的專案中使用了LibClass2和LibClass3。我的專案是全域性啟用了可空取用型別的:
編譯正常,毫無警告和錯誤。一旦執行,則丟擲空取用異常:
可見,目前來說,C#8.0的可空取用型別並不能解決外源性的空值擴散,只能解決內源性的空值擴散,無法跨模組生效。還是洗洗睡吧。
參考資料:
https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/nullable-types/index
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/nullable-reference-types-specification#the-null-forgiving-operator
https://github.com/dotnet/roslyn/blob/master/docs/features/nullable-reference-types.md#setting-project-level-nullable-context
https://stackoverflow.com/questions/54852880/what-is-the-difference-between-nullablecontextoptions-and-nullablereferencetypes
原文地址:https://www.cnblogs.com/zlmdy/p/10656793.html