可為 Null 的參考型別 (C# 參考)
注意
本文涵蓋可為 Null 的參考型別。 您也可以宣告可為 Null 的實值型別。
在已選擇使用可為 Null 感知內容的程式碼中,可以使用可為 Null 的參考型別。 可為 Null 的參考型別、Null 靜態分析警告,以及 Null 容許運算子是選擇性的語言功能。 這些規則預設為關閉。 可為 Null 的內容是在專案層級使用組建設定,或在程式碼中使用 pragmas 控制的。
重要
從 .NET 6 (C# 10) 開始的所有專案範本都會啟用專案的「可為 Null 的內容」。 使用舊版範本建立的專案不包含這個元素,除非您在專案檔中啟用這些功能或使用 pragmas,否則這些功能會關閉。
在可為 Null 的感知內容中:
- 必須以非 Null 初始化參考型別
T
的變數,而且這些變數永遠不會被指派可能為null
的值。 - 可以使用
null
來初始化參考型別T?
的變數,或指派null
,但必須在取值之前先根據null
進行檢查。 - 如
m!
中所示,當您套用 null 容許運算子時,型別m
的變數T?
會被視為非 Null。
編譯器的上述規則解譯會強制執行不可為 Null 的參考型別 T
和可為 Null 的參考型別 T?
之間的差異。 型別 T
的變數和型別 T?
的變數會以相同 .NET 型別表示。 下列範例會宣告不可為 Null 的字串和可為 Null 的字串,然後使用 null 容許運算子將值指派給不可為 Null 的字串:
string notNull = "Hello";
string? nullable = default;
notNull = nullable!; // null forgiveness
變數 notNull
和 nullable
都是以 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-state 追蹤為 not-null 或 maybe-null。 不可為 Null 參考的預設狀態為 not-null。 可為 Null 參考的預設狀態為 maybe-null。
您應可放心對不可為 Null 的參考型別進行取值,因為其 null-state是 not-null。 若要強制執行該規則,如果不可為 Null 的參考型別未初始化為非 Null 值,編譯器就會發出警告。 必須將區域變數指派至其宣告位置。 必須在欄位初始設定式或每個建構函式中為每個欄位指派 not-null 值。 當將不可為 Null 的參考指派給狀態為 maybe-null 的參考時,編譯器會發出警告。 一般而言,不可為 Null 的參考是 not-null,而且對那些變數進行取值時不會發出任何警告。
注意
如果您將 maybe-null 運算式指派給不可為 Null 的參考型別,編譯器會產生警告。 編譯器接著會產生該變數的警告,直到指派給 not-null 運算式為止。
可為 Null 的參考型別可以初始化或指派給 null
。 因此,靜態分析必須判斷在取值之前變數是 not-null。 如果可為 Null 的參考判定為 maybe-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-state。 編譯器會針對 Null 檢查和指派套用語言規則,以通知其分析。 編譯器無法假設方法或屬性的語意。 如果您呼叫執行 Null 檢查的方法,編譯器不知道這些方法會影響變數的 null-state。 這些是您可以新增至 API 的屬性,以通知編譯器引數和傳回值的語意。 這些屬性已套用到 .NET Core 程式庫中的許多常見 API。 例如,已更新 IsNullOrEmpty,且編譯器會正確地將該方法解譯為 Null 檢查。 如需適用於 null-state 靜態分析之屬性的詳細資訊,請參閱可為 Null 屬性的文章。
設定可為 Null 的內容
有兩種方式可以控制可為 Null 的內容。 在專案層級,您可以新增 <Nullable>enable</Nullable>
專案設定。 在單一 C# 來源檔案中,您可以新增 #nullable enable
pragma 以啟用可為 Null 的內容。 請參閱設定可為 Null 策略的文章。 在 .NET 6 之前,新專案使用預設值 <Nullable>disable</Nullable>
。 從 .NET 6 開始,新專案會在專案檔中包含 <Nullable>enable</Nullable>
元素。
C# 語言規格
如需詳細資訊,請參閱 C# 語言規格的下列建議: