日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

C# 中的 null 包容运算符 “!” —— 概念、由来、用法和注意事项

發布時間:2023/12/4 C# 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C# 中的 null 包容运算符 “!” —— 概念、由来、用法和注意事项 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在 2020 年的最后一天,博客園發起了一個開源項目:基于 .NET 的博客引擎 fluss,我抽空把源碼下載下來看了下,發現在屬性的定義中,有很多地方都用到了?null!,如下圖所示:

這是什么用法呢?之前沒有在項目中用過,所以得空就研究了一下。

以前,!?運算符用來表示 “否”,比如不等于?!=。在 C# 8.0 以后,!?運算符有了一個新意義——?null?包容運算符,用來控制類型的可空性。要了解?null?包容運算符,首先就要了解可為 null 的引用類型。

可為 null 的引用類型

C# 8.0 引入了可為 null 的引用類型,與可空類型補充值類型的方式一樣,它們以相同的方式補充引用類型。也就是說,通過將???追加到某引用類型,可以將變量聲明為可以為 null 的引用類型。例如,string??表示可以為?null?的?string。使用這些新類型可以更清楚地表達代碼設計的意圖 —— 比如將某些變量聲明為?必須始終具有值,而其他一些變量聲明為?可以缺少值

借助這個定義,我們在定義引用類型的變量或屬性時,便有了兩種選擇:

  • 假定引用不可以為?null。?當變量定義為不可以為?null?時,編譯器會強制執行規則——確保在不檢查它們是否為?null?的前提下,取消引用這些變量是安全的:

    • 變量必須初始化為非?null?值。

    • 變量永遠不能賦值為?null。

    • 假定引用可以為?null。?當變量定義為可以為?null?時,編譯器會強制執行不同的規則——確保您自己已正確檢查?null?引用:

      • 只有當編譯器可以保證該值不為?null?時,才可以取消引用該變量。

      • 這些變量可以用默認的?null?值進行初始化,也可以在其他代碼中賦值為?null。

      與 C# 8.0 之前對引用變量的處理相比,這個新功能提供了顯著的優勢。在早期版本中,不能通過變量的聲明來確定設計意圖,編譯器沒有為引用類型提供針對?null?引用異常的安全性。

      通過添加可為?null?的引用類型,您可以更清楚地聲明您的意圖。null?值是表示一個變量不引用值的正確方法,請不要使用此功能從代碼中刪除所有的?null?值。而是,應向編譯器和閱讀代碼的其他開發人員聲明您的意圖。通過聲明意圖,編譯器會在您編寫與該意圖不一致的代碼時警告您。

      是不是讀起來有點繞?還是直接看示例比較容易理解些,請繼續往下看。首先,我們來

      啟用可為 null 的引用類型

      有三種方法可以啟用可為 null 的引用類型。

      在項目文件中啟用

      <Nullable>enable</Nullable>

      將上面這一行添加到項目文件中,為當前項目啟用?可為 null 的引用類型,如下圖所示:

      在自定義項目屬性中啟用

      在?Directory.Build.props?文件中可以為目錄下的所有項目啟用?可為 null 的引用類型, 下面截圖是 fluss 項目中的設置:

      使用預處理器指令啟用

      可以使用?#nullable enable?和?#nullable disable?預處理器指令在代碼中的任意位置啟用和禁用?可為 null 的引用類型:

      舉例說明

      典型用法

      假設有這個定義:

      class Person {public string? MiddleName; }

      如下這樣調用:

      void LogPerson(Person person) {Console.WriteLine(person.MiddleName.Length); // 警告 CS8602 解引用可能出現空引用。Console.WriteLine(person.MiddleName!.Length); // 沒有警告 }

      這個?!?運算符基本上就是關閉了編譯器的空檢查。

      內部運行機制

      使用此運算符告訴編譯器可以安全地訪問可能為?null?的內容。您可以用它來表達在這種情況下“不關心”?null?安全性。

      當我們討論到?null?安全性時,一個變量可以有兩種狀態:

    • Nullable : 可以為?null。

    • Non-Nullable :不可以為?null。

    • 從 C# 8.0 開始,所有的引用類型默認都是?Non-nullable。

      “可空性”可以通過以下兩個新的類型運算符進行修改:

    • !?:從 Nullable 改為 Non-Nullable

    • ??:從 Non-Nullable 改為 Nullable

    • 這兩個運算符是相互對應的。您使用這兩個運算符限定變量,然后編譯器根據您的限定來確保?null?安全性。

      ??運算符的用法

    • Nullable:string? x;

      • x?是引用類型,因此默認是不可以為?null?的。

      • 我們使用???運算符將其改為可以為?null?的。

      • x = null;?賦值正常,沒有警告。

    • Non-Nullable:string y;

      • y?是引用類型,因此默認是不可以為?null?的。

      • y = null;?賦值會產生一個警告,因為您給一個聲明為不支持?null?的變量分配了一個?null?值。

      如下圖:

      !?運算符的用法

      string x; string? y = null;
    • x = y;

      • 非法!警告:將 null 文本或可能的 null 值轉換為不可為 null 類型(y?可能為?null)。

      • 賦值運算符?=?左邊是不可以為?null?的,但右邊是可以為?null?的。

    • x = y!;

      • 合法!

      • 賦值運算符?=?左右兩邊都是不可以為?null?的。

      • 因為?y!?使用了?!?運算符到?y,使得右邊也變成了不可以為?null?的,所以賦值沒有問題。

      如下圖:

      ???警告:?null?包容運算符?!?僅在類型系統級別關閉編譯器檢查;在運行時,該值仍然可能是?null。

      這是反模式的

      C# 編程時應該盡量避免使用?null?包容運算符?!。

      有一些有效的使用場景(在下面會介紹),比如單元測試,使用這個運算符是適合的。不過,在 99% 的情況下,使用替代解決方案會更好。請不要只是為了取消警告,而在代碼中打幾十個?!。要想清楚您的場景是否真的值得使用它。

      ???? 可以使用,但要小心使用。如果沒有實際的目的或使用場景,請不要使用它。

      null?包容運算符?!?抵消了您獲得的編譯器保證的?null?安全性的作用!

      使用?!?運算符將導致很難發現 bug。如果您定義了一個標記為不可以為?null?的屬性,您也就假定了可以安全地使用它。但是在運行時,您卻突然遇到?NullReferenceException?異常而撓頭,因為一個值在用?!?繞過了編譯器檢查后,實際上卻變成了?null,這不是給自己添麻煩嗎?

      既然這樣,那么,

      為什么?!?運算符會存在?

      • 在某些邊緣情況下,編譯器無法檢測到可以為 null 的值實際上是不為?null?的。

      • 使遺留代碼庫遷移更容易。

      • 在某些情況下,您根本不關心某些內容是否為?null。

      • 在進行單元測試時,您可能想要檢查傳遞?null?時的代碼行為。

      接下來,我們繼續看下:

      null!?是什么意思呢?

      null!?是在告訴編譯器?null?不是?null?值,這聽起來很怪異,是不是?

      實際上,它和上面例子中的?y!?一樣。它只是看起來挺怪異,因為您將該運算符用在了?null?字面量上,但概念是一樣的。

      我們再來看一下文章開頭提到的 fluss 源碼中的一行代碼:

      /// <summary> /// 所屬的博客。 /// </summary> public BlogSite BlogSite { get; set; } = null!;

      這行代碼定義了一個名稱為?BlogSite、類型為?BlogSite?的不可以為?null?的類屬性。因為它是不可以為?null?的,因此單從技術上講,很明顯它是不可以被賦值為?null的。

      但是,您可以通過使用?!?運算符,將?BlogSite?屬性賦值為?null。因為,就編譯器所關心的?null?安全性而言,null!?不是?null。

      總結

      看到這里,想必您肯定已經明白了?null!?是什么意思,也學會了?null?包容運算符?!?的概念、由來和用法。但是正如我在文中提到的那樣,編程時應該盡量避免使用?!,因為它抵消了您本可以獲得的編譯器保證的?null?安全性;而且,這種寫法閱讀起來有點讓人費解。


      參考:

      • https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references

      • https://stackoverflow.com/questions/54724304/what-does-null-statement-mean

      • https://www.cnblogs.com/cmt/p/14217355.html

      • https://www.meziantou.net/csharp-8-nullable-reference-types.htm

    總結

    以上是生活随笔為你收集整理的C# 中的 null 包容运算符 “!” —— 概念、由来、用法和注意事项的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。