首頁 > 軟體

C#新特性之可空參照型別

2022-02-22 16:01:04

安裝

您必須下載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值或可為空參照型別的值,則會發出警告;
  • 如果直接使用可為空參照型別,則會發出警告;
  • 如果從來沒有給非可為空參照型別的屬性賦值,則會發出警告;
  • 如果需要直接使用可為空參照型別,需要使用 ! 符號告訴編譯器,您已經確認過該值不可能為空。

當然這只是編譯器的行為,可以禁用與之相關的警告提示。

總結

空參照型別是一個語法糖,只是在編譯器的層面幫我們發現可能發生的問題,對程式的正常執行沒有任何作用。

到此這篇關於C#新特性之可空參照型別的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com