可空引用類型是C 8.0計劃新增的一個功能,不過已經發佈了預覽版本,今天我們來體驗一下可空引用類型。 安裝 您必須下載Visual Studio 2017 15.5預覽版(目前最新發佈版本是15.4),下載地址:。 安裝Roslyn擴展預覽版本: 1. 下載並解壓 Roslyn_Nullable_R ...
可空引用類型是C#8.0計劃新增的一個功能,不過已經發佈了預覽版本,今天我們來體驗一下可空引用類型。
安裝
您必須下載Visual Studio 2017 15.5預覽版(目前最新發佈版本是15.4),下載地址:https://www.visualstudio.com/en-us/news/releasenotes/vs2017-preview-relnotes。
安裝Roslyn擴展預覽版本:
- 下載並解壓 Roslyn_Nullable_References_Preview.zip [最新版本 11/15/17];
- 關閉所有運行的Visual Studio;
- 運行zip根目錄中的 .\install.bat 腳本(如果需要卸載擴展,可以運行.\uninstall.bat腳本);
語法與類型
在語法上,可為空引用類型與可為空值類型使用的語法是一致的,在類型後面追加 ? 即可。
class Person
{
public string FirstName;
public string? MiddleName;
public string LastName;
}
我們都知道當初微軟在增加可為空值類型的時候,實際是在框架中增加了System.Nullable<>
類型,您肯定會問,可為空引用類型以框架中又增加了什麼新的類型。
我們來看一個演示:
class Program
{
static void Main(string[] args)
{
Console.WriteLine(typeof(string?).FullName);
}
}
輸出結果:
您是否覺得奇怪,怎麼輸出的是System.String
,是的,其實微軟在框架中沒有加入任何類型,我們Person
類型進行編譯後,再通過dotPeek進行反編譯,就明白到底發生了什麼。
反編譯後的結果:
internal class Person
{
public string FirstName;
[Nullable]
public string MiddleName;
public string LastName;
}
只是在MiddleName
欄位上增加了System.Runtime.CompilerServices.NullableAttribute
標記。
我們來看一看屬性、參數、變數、返回值編譯之前與編譯之後的比對結果。
屬性
// 編譯前:
public string? MiddleName { get; set; }
// 編譯後:
[Nullable]
public string MiddleName { [return: Nullable] get; [param: Nullable] set; }
參數
// 編譯前:
public Person(string? middleName )
{
this.MiddleName = middleName;
}
// 編譯後:
public Person([Nullable] string middleName)
{
this.MiddleName = middleName;
}
返回值
// 編譯前:
public string? DoSomething()
{
return null;
}
// 編譯後:
[return: Nullable]
public string DoSomething()
{
return (string) null;
}
變數
// 編譯前:
string? name;
// 編譯後:
string name;
這裡除了變數,其它的都使用了NullableAttribute
標記進行的修飾。
它可以做什麼?
通過上面的章節,我們知道,可為空引用類型只是在參數、屬性、參數和返回值中使用NullableAttribute
標記進行修飾,實際上對程式的正常運行沒有任何的影響。那麼它可以為我們做什麼呢?
表達意圖
在C#中不能表達這個變數、參數、欄位、屬性,返回值等可能為null
或不能為null
,可為空類型可以幫我們解決這個問題。
class Person
{
public string FirstName; // 不為null
public string? MiddleName; // 可能為null
public string LastName; // 不為null
}
這個類型的可以表示每一個人都應該 FristName 和 LastName ,但是不是每一個人都應該有 MiddleName。
編譯器檢測
可為空引用類型的另一個好處是編譯器可以幫助我們檢測代碼,比如對於直接使用可為空引用類型的屬性,編譯器會發出警告。
void M(Person p)
{
p.FirstName = null; // 1 WARNING: Cannot convert null to non-nullable reference。
p.LastName = p.MiddleName; // 2 WARNING: Possible null reference assignment.
string s = default(string); // 3 WARNING: Cannot convert null to non-nullable reference。
if (p.MiddleName != null)
{
WriteLine(p.MiddleName.Length); // ok
}
WriteLine(p.MiddleName!.Length); // ok
}
class Person
{
public string FirstName; // 4 WARNING: Non-nullable field 'FirstName' is uninitialized.
public string? MiddleName;
public string LastName; // 5 WARNING: Non-nullable field 'LastName' is uninitialized.
}
編譯器會幫我們做以下幾點檢測:
- 如果給非可為空引用類型賦
null
值或可為空引用類型的值,則會發出警告; - 如果直接使用可為空引用類型,則會發出警告;
- 如果從來沒有給非可為空引用類型的屬性賦值,則會發出警告;
- 如果需要直接使用可為空引用類型,需要使用 ! 符號告訴編譯器,您已經確認過該值不可能為空。
當然這隻是編譯器的行為,可以禁用與之相關的警告提示。
總結
空引用類型是一個語法糖,只是在編譯器的層面幫我們發現可能發生的問題,對程式的正常運行沒有任何作用。
參考資料:
https://blogs.msdn.microsoft.com/dotnet/2017/11/15/nullable-reference-types-in-csharp/
https://github.com/dotnet/csharplang/wiki/Nullable-Reference-Types-Preview