很多小夥伴在升級到Visual Studio 2022後發現,如果我們去新建一個.NET 6的專案,和原先VS2019中一摸一樣的寫法,卻會出現CS8618 Non-nullable property或者其他可能為null的警告。雖然不影響程式碼的編譯和執行,卻讓人心裡不踏實。
這是因為VS2019除了.NET Core 3.x的專案型別,都是預設使用C# 7.3版本。而從C# 8.0之後,增加了對可為null型別的檢查,旨在最大程度的減少執行時出現NullReference異常。
除非小夥伴們時刻關心C#版本更新內容,否則這個隨著VS2022和.NET 6預設開啟的新特性,可能帶來的不是驚喜,反而有一點驚嚇。
那麼讓我們來看看如何迎接這個新的改變。這個變化的主旨是希望程式碼中,儘可能不再有為null的情況。首先我們來看最常見的一個Warning,在屬性宣告的時候。
平平無奇的寫法,其中卻蘊藏著兩個Warnings。這種的處理較為簡單,給出明確的初始值即可。例如string.Empty。你可以覺得這是在脫褲子放屁,但是回過頭想一想,在執行過程中FristName和LastName為「」的情況,一定是程式碼有意為之。而不是不知道哪裡返回了一個null,且無意中賦值上去了。
第二種常見的情況,是我們獲得了一個可為null型別的物件,然後直接使用了。下圖中XDocument的Load方法返回了Xelement?型別的物件。如果我們不做null check,就會存在Warning的波浪線。
一般來說這裡加個if判斷,就完事了。至少這個方法本身算是交差了。就像Xdocument.Root這個屬性一樣。但是業務程式碼的設計,和基礎框架API的設計在思路上是有區別的。業務程式碼在業務明確的情況下返回null,會造成一種無限套娃的微妙效果。就像下面這個,到了外面一層的使用方法,仍然要糾結於null check。
從這個方法看,如果我就是一定要獲取Root物件,或者對Root物件做一些不可描述的事情。那麼XML檔案中root節點為空就是一個不可接受,完全錯誤的情況。我個人傾向直接throw NullReferenceException。
另一種相似的情況是,可空型別被作為引數在接下來的程式碼中使用。下面程式碼中的attribute在業務邏輯中,很有可能就不應該存在為null的情況。否則就是輸入的源頭有錯誤。
這時要麼通過if來回避引數為null這個錯誤,要麼乾脆就把錯誤暴露出來。這裡推薦使用ArgumentNullException的靜態方法ThrowIfNull。比先寫個if判斷再throw的方式要省力得多。
Visual Studio通過程式碼靜態分析來判斷是否報告可為null的Warning。但是程式碼寫起來是非常靈活的,所以有時候會有誤報或者分析不出來的情況。比如下面這個例子。
明明已經對相同的物件做了null check,但仍然會有Warning,這是因為Visual Studio並不認為這兩段重複的程式碼是同一個東西。處理的方式也很簡單,一是老老實實通過建立區域性變數的參照來避免重複。
二是通過驚歎號(!)操作符告訴Visual Studio,我確信這裡沒有問題,別逼逼了。所以可以有下面這個寫法。當然我個人並不推薦這麼做,首先是這個寫法不相容之前低版本的C#,萬一要做一個向低版本的移植或相容就麻煩了。其次!操作符有些過度自信的感覺,它沒有實際的意義,僅僅是關閉了這個Warning,頗有掩耳盜鈴的意思。
以上就是今天想和大家討論的,關於遷移到Visual Studio 2022和C# 10以後,遇到的可為null型別的問題。持續性的學習不是說要每個小版本都緊跟潮流,而是在每一次的新專案,新團隊,新機遇到來的時候,即時地更新自己。一點淺見,拋磚引玉。
以下連結,是MS Learn上Windows開發的入門課程,單個課程三十分鐘到60分鐘不等,想要補充基礎知識的同學點這裡:
開始使用 Visual Studio 開發 Windows 10 應用