C# 8.0可空引用類(lèi)型的使用注意記錄
前言
最近VS2019正式版發(fā)布了,裝下來(lái)順便試用了一下C#8.0,最大的看點(diǎn)應(yīng)該就是可空引用類(lèi)型了。不過(guò)C#8.0仍然處于Beta的狀態(tài),而且試用時(shí)也遇到了幾個(gè)坑。
背景知識(shí)說(shuō)明:
所謂的可空引用類(lèi)型是指,一旦啟用了可空引用類(lèi)型這個(gè)新特征,引用類(lèi)型將默認(rèn)被視為不可空,無(wú)法賦予null,除非手工將它設(shè)為可空引用類(lèi)型。
實(shí)戰(zhàn)示例:
首先是新建一個(gè)C#的項(xiàng)目,在 項(xiàng)目文件(.csproj)里加入兩行配置,目的是啟用“C#8.0語(yǔ)言”和“可空引用類(lèi)型”:
<LangVersion>8.0</LangVersion> <NullableContextOptions>enable</NullableContextOptions>
整個(gè)文件看起來(lái)是這樣的:

這樣就算是整個(gè)項(xiàng)目全局啟用了可空引用類(lèi)型了。
注意:
在VS2019正式版中,使用
<NullableContextOptions>enable</NullableContextOptions>
而不是使用
<NullableReferenceTypes>true</NullableReferenceTypes>
后者在正式版中已經(jīng)失效了。
如果不希望全局啟用可空引用類(lèi)型的話,可以在程序代碼中加入以下編譯指令:
#nullable enable
這樣可以在加入了該指令的文件中,單獨(dú)啟用可空引用類(lèi)型。但是,極度不推薦這種做法。為什么呢?因?yàn)樵撝噶顑H僅在該文件中有效,不能跨文件生效,從而無(wú)法阻止null逃逸到使用了該指令的文件中,也就是說(shuō),用了也等于沒(méi)用。
一個(gè)很簡(jiǎn)單的例子足以證明:

注意上面項(xiàng)目文件中并沒(méi)有全局啟用可空引用類(lèi)型,而下面的Class1.cs中使用了編譯器指令來(lái)單獨(dú)啟用可空引用類(lèi)型。


從運(yùn)行結(jié)果可見(jiàn),空引用仍然逃逸到使用了該指令的作用域中了。別說(shuō)編譯錯(cuò)誤,連編譯警告都沒(méi)有。完全達(dá)不到理想的效果。

因此,強(qiáng)烈建議在項(xiàng)目文件中全局啟用可空引用類(lèi)型,而不是在某個(gè)源文件中單獨(dú)使用。
另外,還有一點(diǎn)要注意的是,即使啟用了可空引用類(lèi)型后,默認(rèn)情況下,即使對(duì)不可空引用賦予null,編譯器也只會(huì)生成編譯警告,而不是編譯錯(cuò)誤。仍然是能夠編譯通過(guò)的。一個(gè)大項(xiàng)目中,編譯警告不可避免,甚至可能會(huì)很多,從而淹沒(méi)了“給不可空引用類(lèi)型賦予空值”這種不起眼的警告。
因此,建議將特定的警告視為錯(cuò)誤。警告編號(hào)為8600、8625、8618、8604,或者將所有警告視為錯(cuò)誤。具體是在項(xiàng)目文件中加入以下設(shè)置(見(jiàn)圖一):
<WarningsAsErrors>8600 8625 8618 8604</WarningsAsErrors>
或者在項(xiàng)目編輯器中設(shè)置也可以:

這是我自己測(cè)試得出的結(jié)果,可能還有其它潛在的相關(guān)警告編號(hào)我沒(méi)有測(cè)試出來(lái)。如果有誰(shuí)知道的話,告訴我一下,謝謝。
做好這些配置之后,可以看到引用類(lèi)型默認(rèn)都不能賦予空值了:

這時(shí)候普通的引用類(lèi)型的變量和參數(shù)都不能設(shè)為null了。
這樣可以防止空值擴(kuò)散開(kāi)來(lái),引起惱人的空引用異常。
但是,這里有個(gè)坑需要注意?。。?!
struct里不適用可空引用的規(guī)則?。?/p>
struct里不適用可空引用的規(guī)則?。?/p>
struct里不適用可空引用的規(guī)則?。?/p>

這種情況下直接運(yùn)行,仍然會(huì)拋出空引用異常?。?!C#8.0仍未能完全封堵住空引用的逃逸。

其實(shí)我還是比較贊同用不可空引用類(lèi)型的方案的,而不是可空引用類(lèi)型的方案。畢竟我想要的,只不過(guò)是一個(gè)不可空的斷言,只是想利用不可空引用來(lái)劃分安全邊界,從而防止空值的擴(kuò)散。簡(jiǎn)單來(lái)說(shuō)就是想將變量和參數(shù)明確聲明為不可空引用類(lèi)型。因?yàn)闅v史和現(xiàn)實(shí)的原因,大量的庫(kù)都還沒(méi)能全面使用可空引用類(lèi)型。這種情況下,只有我使用可空引用類(lèi)型,是不靠譜的。無(wú)法劃分安全邊界。
然而C#選擇了可空引用類(lèi)型的方案,而且還不是強(qiáng)制啟用,而且默認(rèn)只是警告。。跟沒(méi)有一樣。。。
比方說(shuō),我使用了一個(gè)第三方庫(kù)項(xiàng)目,而空值的來(lái)源是正好是該庫(kù)項(xiàng)目的,而我對(duì)這個(gè)庫(kù)并沒(méi)有源代碼或者修改權(quán)限。這時(shí)候就無(wú)法阻止空值逃逸到我的項(xiàng)目中了。
還是之前的代碼,只是稍微做一下修改。新增了一個(gè)庫(kù)項(xiàng)目ClassLibrary1,這個(gè)庫(kù)并沒(méi)有使用可空引用類(lèi)型。

庫(kù)的代碼如下:

很簡(jiǎn)單,就是LibClass3.GetInstance()本應(yīng)該返回LibClass2的實(shí)例,但是出于某種原因,返回了null。但是我的項(xiàng)目中使用了LibClass2和LibClass3。我的項(xiàng)目是全局啟用了可空引用類(lèi)型的:


編譯正常,毫無(wú)警告和錯(cuò)誤。一旦運(yùn)行,則拋出空引用異常:

可見(jiàn),目前來(lái)說(shuō),C#8.0的可空引用類(lèi)型并不能解決外源性的空值擴(kuò)散,只能解決內(nèi)源性的空值擴(kuò)散,無(wú)法跨模塊生效。還是洗洗睡吧。
參考資料:
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
https://github.com/dotnet/roslyn/blob/master/docs/features/nullable-reference-types.md
https://www.youtube.com/watch?v=VdC0aoa7ung
https://stackoverflow.com/questions/54852880/what-is-the-difference-between-nullablecontextoptions-and-nullablereferencetypes
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
C#編寫(xiě)網(wǎng)游客戶(hù)端的實(shí)現(xiàn)
本文主要介紹了C#編寫(xiě)網(wǎng)游客戶(hù)端,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11
Unity實(shí)現(xiàn)繞任意軸任意角度旋轉(zhuǎn)向量
這篇文章主要為大家詳細(xì)介紹了Unity實(shí)現(xiàn)繞任意軸任意角度旋轉(zhuǎn)向量,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-01-01
C# Winform實(shí)現(xiàn)導(dǎo)入和導(dǎo)出Excel文件
這篇文章主要為大家詳細(xì)介紹了C# Winform實(shí)現(xiàn)導(dǎo)入和導(dǎo)出Excel文件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12
asp.net core 使用 tensorflowjs實(shí)現(xiàn) face recognition的源代碼
tensorflowjs,在該項(xiàng)目中使用了ml5js這個(gè)封裝過(guò)的機(jī)器學(xué)習(xí)JavaScript類(lèi)庫(kù), 使用起來(lái)更簡(jiǎn)單,本文給大家分享asp.net core 使用 tensorflowjs實(shí)現(xiàn) face recognition的源代碼,需要的朋友參考下吧2021-06-06
用C#對(duì)ADO.NET數(shù)據(jù)庫(kù)完成簡(jiǎn)單操作的方法
用C#對(duì)ADO.NET數(shù)據(jù)庫(kù)完成簡(jiǎn)單操作的方法...2007-03-03
C#使用CEFSharp獲取動(dòng)態(tài)網(wǎng)頁(yè)源碼的演示步驟
CEFSharp是一個(gè)用C#編寫(xiě)的庫(kù),它是Chromium Embedded Framework (CEF) 的.NET封裝和擴(kuò)展,CEF允許開(kāi)發(fā)者在自己的應(yīng)用程序中嵌入一個(gè)功能強(qiáng)大的HTML渲染引擎,從而能夠呈現(xiàn)網(wǎng)頁(yè)內(nèi)容,本文介紹了C#如何使用CEFSharp獲取動(dòng)態(tài)網(wǎng)頁(yè)源碼,需要的朋友可以參考下2024-08-08

