C# 14 中的新字段關鍵字
C#中的自動屬性簡潔,但當您需要在setter中加入驗證或轉換邏輯時,就不得不完全放棄它們,並撰寫一個具有手動輔助欄位的完整屬性。 從一行跳到七行是為了添加單個保護子句而付出的高昂代價。 C# 14引入field關鍵字來填補這一差距,讓您可以在編譯器仍管理輔助欄位的情況下自訂getter或setter。
在他的影片《C# 14的新field關鍵字》中,Tim Corey展示了這個功能解決的問題,並通過具體的setter驗證範例講解,還介紹了升級前應了解的命名衝突。 我們將詳細遵循每一步驟,以便您可以自信地在自己的屬性中使用field。
設置:一個簡單的Person模型
[0:12 - 1:07] Tim從在.NET 10和 Visual Studio 2026上執行的控制台應用程式開始。演示主要聚焦於一個具有若干屬性的Person類:
public required string FirstName { get; set; }
public required string LastName { get; set; }
public int Age { get; set; }
public required string FirstName { get; set; }
public required string LastName { get; set; }
public int Age { get; set; }
還有一個由私有欄位支援的Demo屬性,當命名衝突出現時變得相關。 在LastName = "Corey"創建一個實例,然後打印姓氏、年齡和演示值。 一切如預期輸出:'Corey',0(默認整數),和 'test'。
問題:自動屬性接受不良數據
[1:23 - 2:49] 問題在於Tim在結構後將LastName時出現:
p.LastName = null;
p.LastName = null;
即使required並被類型定義為不可為空的字串,賦值仍可編譯。 required修飾符僅強制在物件初始化期間提供值; 它不會阻止他人在之後將屬性設置為null。 結果是在運行時出現空白姓氏且未拋錯誤。
這在數據完整性上是一個真正的漏洞。 類型系統會通過可空性引用波浪線警告您,但這僅是編譯時提示而非運行時保護。 如果您的應用程式依賴於LastName始終包含有效字串,僅靠自動屬性無法強制執行該契約。
舊的解決方案:具有手動輔助欄位的完整屬性
[2:58 - 4:19] 在C# 14之前,標準解決方案是將自動屬性轉換為具有顯式輔助欄位的完整屬性:
private string _lastName;
public required string LastName
{
get => _lastName;
set => _lastName = value ?? throw new ArgumentNullException(nameof(LastName));
}
private string _lastName;
public required string LastName
{
get => _lastName;
set => _lastName = value ?? throw new ArgumentNullException(nameof(LastName));
}
Tim運行這個並確認異常正確地觸發:'值不能為空。 參數名稱:LastName。' 這種方法有效,但它需要聲明一個私有欄位,並連接getter和setter,並在多行中重複屬性名稱。 對於單個驗證規則,這是一種繁鎖的程式。
在這種情況下,getter沒有執行特殊操作; 它不改變地返回欄位的值。 然而您仍必須明確撰寫它,因為一旦您離開自動屬性範疇,語法便要求兩部分同時存在。 Tim將這種繁瑣視為新功能背後的動機。
C# 14解決方案:field關鍵字
[4:23 - 5:47] C# 14引入了一個中間地帶。 不再需要自行聲明私有輔助欄位,您可以在getter或setter中使用上下文關鍵字field來直接引用由編譯器生成的輔助欄位:
public required string LastName
{
get;
set => field = value ?? throw new ArgumentNullException(nameof(LastName));
}
public required string LastName
{
get;
set => field = value ?? throw new ArgumentNullException(nameof(LastName));
}
getter保持為自動實作get;,不需要主體。 setter使用value。 編譯器在幕後創建並管理輔助欄位,就像標準自動屬性一樣。
運行演示在空值賦值時產生同樣的ArgumentNullException。 行為與手動支援版本相同,從七行壓縮到僅自訂所需的精簡區塊。 您保留自動屬性getter,僅在setter中加入邏輯,並完全跳過手動欄位聲明。
這提供了一個實用的中間步驟,介於簡單自動屬性(一行,無驗證)和完整屬性(七行或更多,完全控制)之間。 當您的邏輯僅觸及setter時,您不再需要支付重寫getter的語法代價。
使用Setter保障驗證年齡
[6:16 - 7:39] 為了展示Age屬性添加了範圍驗證:
public int Age
{
get;
set
{
if (value > 0 && value < 120)
field = value;
}
}
public int Age
{
get;
set
{
if (value > 0 && value < 120)
field = value;
}
}
此處setter默默地忽略掉位於合理範圍之外的值。 將field從未被寫入。 Tim指出,您可以拋出異常,但這種靜默方法演示了setter主體可以包含您需要的任何邏輯,同時仍依賴於field進行存儲。
模式廣泛適用:約束數字範圍,修剪字串中的空白,標準化大小寫,或是每次設置屬性時應用的任何轉換。
與現有field變數的命名衝突
[7:39 - 9:43] Tim介紹了一個刻意設置的邊緣案例。 演示類有一個字面名稱為field的私有成員:
private string field = "test";
private string field = "test";
一旦C# 14有效,編譯器將屬性存取器中的field視為關鍵字而非變數。 這意味著參考field的屬性會默默從該屬性後面的隱藏存儲(空的)中讀取,而不是含有'test'的字串成員。 輸出變為空白且無編譯錯誤,僅有警告。
存在兩種解決方案。加上this.field前綴能告訴編譯器您指的是類級成員,而非關鍵字。 或者,@field逃逸符號同樣有效:
// Both refer to the instance variable, not the keyword
string demo => this.field;
string demo => @field;
// Both refer to the instance variable, not the keyword
string demo => this.field;
string demo => @field;
Tim強烈建議在升級到C# 14時重命名任何稱為field的變數。在您的IDE中快速'全部重命名'即可永久消除歧義。 衝突只會在屬性存取器內部發生; 構造函數和方法會將field解析為變數名稱,因為這些上下文中沒有隱式輔助存儲。
總結:更少的樣板,但同樣的控制
[10:04 - 10:28] field關鍵字填補了日常C#程式碼中的一個實用缺口。 需要一個保護子句或轉換的屬性不再需要全面重寫,使用手動輔助欄位。 您只自訂需要邏輯的存取器,而將另一個作為標準自動實作。
結論
[10:28 - 10:35] 回顧:C# 14的field關鍵字為您提供了直接存取任何屬性存取器內隱式輔助存儲的能力。 使用它來添加setter驗證、getter轉換或兩者兼有,而不必為不需要客製化的部分放棄自動屬性語法。
在升級之前,請在您的原始碼庫中搜尋任何名為field的變數並重命名它們。 那樣的小心能避免此功能引入的唯一潛在問題。 除此之外,它是一種乾淨的樣板減少,完全適合大多數開發者已經結構化其模型的方式。
範例提示:如果您只需要驗證setter,則將getter保持為一個沒有主體的普通get;。 編譯器會將其當成自動屬性getter處理,您無需撰寫無意義的傳遞回傳語句。
