Visual C# 8.0中引入了可空引用型別(Nullable reference type),通過編譯器提供的強大功能,幫助開發人員儘可能地規避由空引用帶來的程式碼問題。這裡我大致介紹一下可空引用型別的基本內容。
剛開始接觸這個語言特性的時候,可能會不太容易理解。引用型別本來不就是可以為空(null)的麼,為啥還要特別地引入“可空引用型別”的概念呢?其實這是從編譯器的角度要求開發人員在程式設計的時候就考慮某個變數是否有可能為空,從而儘可能地減少由空引用所帶來的程式碼錯誤。
假設有如下類:
class Student
{
public Student(string name, DateTime dayOfBirth)
=> (Name, DayOfBirth) = (name, dayOfBirth);
public string Name { get; set; }
public DateTime DayOfBirth { get; set; }
public string Notes { get; set; }
}
此類定義了一個“學生”實體的基本資訊,為了簡化起見,這裡只列出了需要討論的幾個屬性:
- Name:學生姓名
- DayOfBirth:學生生日
- Notes:對學生資訊的一些備註
假設我們有兩個操作:在所有學生中,找出所有具有備註資訊的學生,以及對所有學生按姓名排序,在C#中很容易使用Linq來實現:
var studentsHasNotes = students.Where(s => s.Notes.Length > 0);
以及:
var orderedStudents = students.OrderBy(s => s.Name);
到目前為止沒啥問題,程式能夠正常執行。然而仔細進行程式碼審查不難發現,在獲取所有具有備註資訊的學生的程式碼中(也就是上面第一段程式碼中),有可能出現空引用的異常,因為對於一個“學生”實體來說,它的Notes屬性是有可能為null的。
現在我們開啟“可空引用型別”這一語言特性,開啟方式主要有兩種:可以在專案級別,編輯csproj專案檔案進行設定,也可以通過#nullable預編譯指令來實現:
- 編輯csproj專案檔案,加入
<Nullable>enable</Nullable>
即可:
- 通過#nullable預編譯指令來實現,只需要在程式碼中需要的地方加入#nullable指令即可:
啟用“可空引用型別”這一語言特性之後你會發現,在上面的Student類的建構函式處出現了一個警告,提示在建構函式執行完成時,不可為空的“Notes”屬性需要有一個不為空的值,建議將其設定為可空的string型別。為什麼編譯器僅提示Notes有可能為空,而不是Name屬性呢?因為建構函式中已經為Name賦值了,因此,對於任何一個Student的物件,Name不可能為空,而Notes則不然。
Name不可能為空?它不是string型別麼?萬一在程式碼中它為空了怎麼辦?別急,編譯器是不會允許出現這種情況的:
在此,我們將Notes屬性設定為string?
型別,於是你會發現,位於建構函式上的警告資訊已經沒有了,因為我們允許Student物件可以沒有Notes資料,但在“找出所有具有備註資訊的學生”這一操作時,又會出現警告,提示說Notes有可能為空:
於是,你會發現,在啟用了可空引用型別的語言特性後,我們就需要仔細考察Student型別中的每一個引用型別的屬性,看它在實際應用中是否有可能為空,如果可能為空,則用可空引用型別來定義屬性,之後編譯器就會幫助你來分析哪些地方有可能存在空引用。
在上面的“找出所有具有備註資訊的學生”例子中,如果你覺得Notes肯定不會為空,那麼也可以使用“!”操作符來覆蓋編譯器的警告資訊,比如:
現在流行的.NET開源框架基本上都已經支援了可空引用型別了,而且如果你是一名開源框架的開發人員,也強烈建議在你的框架中啟用這一語言特性來儘可能地避免空引用問題。比如,如果你在程式碼中啟用了可空引用型別特性,那麼當你從Newtonsoft.Json的JsonConverter類繼承時,你會發現,你必須使用可空引用型別的函式過載:
但如果你沒有啟用可空引用型別特性,那麼當你從Newtonsoft.Json的JsonConverter類繼承時,你會發現,過載函式的簽名與以前一樣:
好了,對於C# 8.0的“可空引用型別”大致就介紹這麼多,相信應該已經基本上概括了它的要點和使用方式,在日常開發中應該夠用了。