C# 參考 (可為 Null 的參考型別)

注意

本文涵蓋可為 Null 的參考型別。 您也可以宣告 可為 Null 的實值型別

從 C# 8.0 開始,在已加入宣告 可為 Null 感知內容的程式碼中,可以使用可為 Null 的參考型別。 可為 Null 的參考型別、Null 靜態分析警告,以及 Null 表示運算子 是選擇性語言功能。 預設會關閉所有專案。 可為 Null 的內容是在專案層級使用建置設定,或在程式碼中使用 pragmas 來控制。

重要

從 .NET 6 開始的所有專案範本 (C# 10) 啟用專案的 可為 Null 內容 。 使用舊版範本建立的專案不包含這個專案,除非您在專案檔中啟用這些功能或使用 pragmas,否則這些功能會關閉。

在可為 Null 的感知內容中:

  • 參考型 T 別的變數必須以非 Null 初始化,而且永遠不會指派可能是 null 的值。
  • 參考型 T? 別的變數可以使用 或指派 null 初始化 null ,但必須在取消參考之前先檢查 null
  • 當您套用 null 表示運算子時,類型 T? 變數 m 會被視為非 Null,如 中所示 m!

編譯器解譯上述規則會強制執行不可為 Null 的參考型 T 別與可為 Null 的參考型 T? 別之間的差異。 型 T 別的變數和 類型的 T? 變數會以相同的 .NET 類型來表示。 下列範例會宣告不可為 Null 的字串和可為 Null 的字串,然後使用 null 表示運算子將值指派給不可為 Null 的字串:

string notNull = "Hello";
string? nullable = default;
notNull = nullable!; // null forgiveness

變數 notNullnullable 都是以 String 型別表示。 由於不可為 Null 和可為 Null 的型別都儲存為相同的類型,因此不允許使用可為 Null 的參考型別。 一般而言,可為 Null 的參考型別不能當做基類或實作的介面使用。 可為 Null 的參考型別不能用於任何物件建立或類型測試運算式。 可為 Null 的參考型別不能是成員存取運算式的類型。 下列範例顯示下列建構:

public MyClass : System.Object? // not allowed
{
}

var nullEmpty = System.String?.Empty; // Not allowed
var maybeObject = new object?(); // Not allowed
try
{
    if (thing is string? nullableString) // not allowed
        Console.WriteLine(nullableString);
} catch (Exception? e) // Not Allowed
{
    Console.WriteLine("error");
}

可為 Null 的參考和靜態分析

上一節中的範例說明可為 Null 參考型別的本質。 可為 Null 的參考型別不是新的類別類型,而是現有參考類型的批註。 編譯器會使用這些批註來協助您在程式碼中尋找潛在的 Null 參考錯誤。 不可為 Null 的參考型別與可為 Null 的參考型別之間沒有執行時間差異。 編譯器不會針對不可為 Null 的參考型別新增任何執行時間檢查。 優點是在編譯時間分析中。 編譯器會產生警告,協助您在程式碼中尋找並修正潛在的 Null 錯誤。 宣告您的意圖,編譯器會在程式碼違反該意圖時警告您。

在可為 Null 的啟用內容中,編譯器會對任何參考類型的變數執行靜態分析,同時可為 Null 和不可為 Null。 編譯器會將每個參考變數的 Null 狀態 追蹤為 not-null可能為 null。 不可為 Null 參考的預設狀態 不是 Null。 可為 Null 參考的預設狀態 可能是 null

不可為 Null 的參考型別應該一律安全取值,因為其Null 狀態不是 Null。 若要強制執行該規則,如果不可為 Null 的參考型別未初始化為非 Null 值,編譯器就會發出警告。 區域變數必須指派宣告的位置。 每個欄位都必須在欄位初始化運算式或每個建構函式中指派 非 Null 值。 編譯器會在將不可為 Null 的參考指派給 狀態為 null的參考時發出警告。 一般而言,不可為 Null 的參考 不是 Null ,而且當這些變數被取值時不會發出任何警告。

注意

如果您將 可能為 Null 的運算式指派給不可為 Null 的參考型別,編譯器會產生警告。 然後,編譯器會產生該變數的警告,直到指派給 非 Null 運算式為止。

可為 Null 的參考型別可以初始化或指派給 null 。 因此,靜態分析必須判斷變數在取值之前 不是 Null 。 如果可為 Null 的參考判斷為 可能為 Null,則指派給不可為 Null 的參考變數會產生編譯器警告。 下列類別顯示這些警告的範例:

public class ProductDescription
{
    private string shortDescription;
    private string? detailedDescription;

    public ProductDescription() // Warning! shortDescription not initialized.
    {
    }

    public ProductDescription(string productDescription) =>
        this.shortDescription = productDescription;

    public void SetDescriptions(string productDescription, string? details=null)
    {
        shortDescription = productDescription;
        detailedDescription = details;
    }

    public string GetDescription()
    {
        if (detailedDescription.Length == 0) // Warning! dereference possible null
        {
            return shortDescription;
        }
        else
        {
            return $"{shortDescription}\n{detailedDescription}";
        }
    }

    public string FullDescription()
    {
        if (detailedDescription == null)
        {
            return shortDescription;
        }
        else if (detailedDescription.Length > 0) // OK, detailedDescription can't be null.
        {
            return $"{shortDescription}\n{detailedDescription}";
        }
        return shortDescription;
    }
}

下列程式碼片段顯示編譯器在使用這個類別時發出警告的位置:

string shortDescription = default; // Warning! non-nullable set to null;
var product = new ProductDescription(shortDescription); // Warning! static analysis knows shortDescription maybe null.

string description = "widget";
var item = new ProductDescription(description);

item.SetDescriptions(description, "These widgets will do everything.");

上述範例示範編譯器的靜態分析如何決定參考變數的 Null 狀態 。 編譯器會針對 Null 檢查和指派套用語言規則,以通知其分析。 編譯器無法假設方法或屬性的語意。 如果您呼叫執行 Null 檢查的方法,編譯器不知道這些方法會影響變數的 Null 狀態。 您可以新增至 API 的屬性,以通知編譯器引數的語意和傳回值。 這些屬性已套用至 .NET Core 程式庫中的許多常見 API。 例如,已更新, IsNullOrEmpty 編譯器會正確地將該方法解譯為 Null 檢查。 如需適用于 Null 狀態 靜態分析之屬性的詳細資訊,請參閱 可為 Null 屬性的文章。

設定可為 Null 的內容

有兩種方式可以控制可為 Null 的內容。 在專案層級,您可以新增 <Nullable>enable</Nullable> 專案設定。 在單一 C# 原始程式檔中 #nullable enable ,您可以新增 pragma 來啟用可為 Null 的內容。 請參閱設定 可為 Null 策略的文章。 在 .NET 6 之前,新專案會使用預設值 。 <Nullable>disable</Nullable> 從 .NET 6 開始,新專案會在 <Nullable>enable</Nullable> 專案檔中包含 專案。

C# 語言規格

如需詳細資訊,請參閱 C# 語言規格的下列提案:

另請參閱