C# 14與.NET10新功能 - 3
這是《C# 14與.NET10新功能》系列文章的第三篇,我們將介紹C# 14版新語法,如何運用新語法來撰寫更簡潔、更安全的程式碼。
C# 14版新功能
C# 14版的設計哲學是消除開發過程中的「雜訊」,讓程式碼更貼近其業務邏輯。此版本著重於減少樣板程式碼,同時提高程式碼的可讀性和安全性,讓開發者能以更少的程式碼完成更多的工作。接下來,我們將深入探索 C# 14版帶來的語法革新。
使用「field」關鍵字:不必手動宣告備援欄位(Passkey)登入
在過去,如果我們想在一個自動實作屬性 (auto-implemented property)的「set」存取子中加入驗證邏輯(例如,檢查值是否為 null),就必須手動宣告一個私有的備援欄位 (private backing field)來儲存屬性的值。這個過程不僅繁瑣,也讓程式碼顯得臃腫。
C# 14版新增了一個「field」關鍵字,完美地解決了這個痛點。讓你可以直接在「set」或「get」屬性存取子中,存取由編譯器自動產生的備援欄位。
以下使用Visual Studio 2026開發工具建立一個主控台應用程式(Console App)專案來進行示範,請參考下圖所示:
圖 1:建立主控台專案(Console App)
架構選取「.NET 10.0」,請參考下圖所示:
圖 2:選取「.NET 10.0」架構
讓我們順便看看屬性程式碼語法的演進過程,在C# 13及更早版本宣告屬性時,要手動定義屬性的備援欄位。例如以下程式碼,在專案「Program.cs」檔案加入「Employee」類別,並手動宣告一個私有備援欄位「_name」,在「get」、「set」 存取子中讀寫這個欄位:
using
System;
Employee emp = new Employee();
emp.Name
= "Mary";
Console.WriteLine(emp.Name);
public class
Employee
{
private string _name;
public string Name
{
get => _name;
set => _name = value;
}
}
C# 14版的新作法是使用「field」關鍵字, 無需手動宣告屬性的備援欄位,直接使用「field」關鍵字代表這個備援欄位,可將上述程式改寫如下:
using
System;
Employee emp = new Employee();
emp.Name
= "Mary";
Console.WriteLine(emp.Name);
public class
Employee
{
public string Name
{
get => field;
set => field = value;
}
}
這項改進顯著減少了程式碼行數,讓屬性的語法更為簡潔,也比自動屬性的語法有更多的靈活性。
由於「field」被拿來當作C#關鍵字了,若恰巧有一個欄位名稱就叫「field」,那該怎麼辦呢?此時我們可以使用「this」或「@field」來存取備援欄位,例如修改「Program.cs」檔案程式碼如下:
Employee
emp
= new
Employee();
emp.Name
= "Mary";
Console.WriteLine(emp.Name);
public class
Employee {
private string? field;
public string Name {
get => $"{@field} ,
backing field : {field}";
set {
field = value;
this.field = $"this.field
: {value} ";
}
}
}
這個範例的執行結果請參考下圖所示:
圖 3:使用「this」或「@field」來存取備援欄位
Null條件指派:更優雅地處理 Null 值
「NullReferenceException」例外錯誤是開發中最常見的錯誤之一。為了避免這個錯誤,我們經常需要在使用物件之前先利用「if」判斷式來檢查變數是否為「null」。
C# 14版則擴充了 Null 條件運算子 (?.)的能力,現在它可以用於指派運算子(=符號)的左邊。這表示你可以只用一行程式碼就可安全地完成「如果物件不為 null,就指派一個值給屬性」的操作。這個語法不僅讓程式碼更加精簡,也從根本上提升了程式碼的安全性,讓處理可能為 null 的物件時語法上更加直覺和優雅。
參考以下範例程式碼:
using
System;
class
Employee
{
public int Id { get; }
public string? Name { get; set; }
public string? Info { get; set; }
public Employee(int id, string name)
{
Id = id;
Name = name;
}
}
class
Program
{
static string GetEmployeeInfo(string context)
{
Console.WriteLine($"GetEmployeeInfo()
執行於: {context}");
return $"{context} - 員工資訊";
}
static void Main()
{
Console.WriteLine("C#
14 安全屬性指派範例 (Employee.Info)\n");
// 傳統寫法 (C# 13版及更早版本)
Console.WriteLine("情境 1:employee 不為 null - 傳統 if 檢查");
var employee1 = new Employee(1, "Alice");
if (employee1 is not null)
{
employee1.Info = GetEmployeeInfo("傳統 -
employee1");
}
Console.WriteLine($"員工 {employee1.Id} ({employee1.Name}) Info: {employee1.Info ?? "<null>"}\n");
// C# 14版新作法 (使用 ?.= 指派)
Console.WriteLine("情境 2:employee 不為 null -
C# 14 ?.= 指派");
var employee2 = new Employee(2, "Bob");
employee2?.Info = GetEmployeeInfo("?.=
指派 - employee2");
Console.WriteLine($"員工 {employee2.Id} ({employee2.Name}) Info: {employee2.Info ?? "<null>"}\n");
// employee 為 null - 傳統 if 檢查
Console.WriteLine("情境 3:employee 為 null - 傳統 if 檢查");
Employee? employee3 = null;
if (employee3 is not null)
{
employee3.Info = GetEmployeeInfo("傳統 -
null");
}
else
{
Console.WriteLine("傳統:因 employee
為 null,略過屬性指派\n");
}
// employee 為 null - C#
14 ?.= 指派
Console.WriteLine("情境 4:employee 為 null -
C# 14 ?.= 指派");
Employee? employee4 = null;
employee4?.Info = GetEmployeeInfo("?.=
指派 - null");
Console.WriteLine("如果上方未看到
GetEmployeeInfo 執行訊息,代表 null 狀況下不會執行右側指派。");
}
}
這個範例執行的結果請參考下圖所示:
圖 4:Null 條件運算子 (?.)的Null條件指派
擴充成員:不僅是方法,現在還有屬性
擴充方法(Extension methods)是一個強大的功能,它允許我們為現有的型別不透過物件導向的繼承功能來「添加」新的方法。而C# 14版引入了全新的 「extension」 區塊語法,不僅提供了一種更清晰的擴充方法定義語法,還新增的擴充屬性 (Extension Properties)。
我們可以這麼說:C# 14版新增的「extension」區塊語法,允許開發人員定義擴充成員 (Extension members),其中包含擴充方法(Extension Methods)、擴充運算子(Extension Operators)以及擴充屬性 (Extension Properties)。簡單的使用一句話說明如下:
Extension 區塊
= 把所有
extension(擴充語法)集中寫在一起的區塊
C# 14版終於可以寫出更為直觀的「擴充某個類型的功能」的程式碼。這項改進之所以重要,是因為它允許你以更自然、更具可讀性的屬性語法(變數名稱.屬性名稱)來擴充現有型別,而不是使用方法呼叫語法(變數名稱.方法名稱()),讓你的程式碼更簡潔、更易懂。
在了解使用新的「extension」區塊語法來擴充成員之前,回顧一下傳統寫法,在過去要定義擴充方法(Extension Method)時一定要具備以下條件:
◼將方法的定義放在一個靜態類別(static class)區塊之中
◼方法必須定義為靜態(static)方法
◼方法必須使用「this」關鍵字來修飾第一個參數代表想擴充的型別
我們來看看一下傳統擴充方法的範例,參考以下程式碼擴充內建的「string」型別,為其加上一個「Print」擴充方法,以便將字串輸出到主控台,叫用這個方法時,需要加上小括號「msg.Print()」:
using
System;
using
System.Collections.Generic;
using
System.Linq;
public static class
StringExtensions
{
public static void Print(this string s)
{
Console.WriteLine(s.ToUpper());
}
}
class
Program
{
static void Main()
{
string msg = "Hello";
msg.Print();
}
}
C# 14版的新寫法是使用「extension」區塊(Extension Block),此外過去 C# 不能擴充屬性,只能擴充方法。例如使用新語法改寫上例的程式碼如下,「extension」關鍵字後方的小括號 () 中描述想要擴充「string」型別,此外在「extension」區塊中宣告「Print」方法時,可以省略掉前版的「this string s」參數,以及「static」關鍵字:
/// <summary>
/// C# 14 擴充方法範例
/// </summary>
public static class
StringExtensions {
// 靜態擴充區塊:僅指定接收類型 (string),沒有參數名稱
extension(string s) {
// 靜態擴充方法:將字串轉為大寫並列印
public void Print() => Console.WriteLine(s.ToUpper());
}
}
class
Program {
static void Main() {
string msg = "Hello";
//使用擴充方法
msg.Print();
//也可以直接呼叫靜態擴充方法
StringExtensions.Print(msg);
}
}
泛型型別參數(Generic Type Parameter)
若想擴充的方法會使用到泛型型別參數(Generic Type Parameter)時,可以在「extension」關鍵字後方的<>符號中加上參數名稱,例如底下的範例程式碼擴充「IList<T>」型別,新增一個「Print()」方法,可以將型別中的所有字串轉成大寫後輸出到主控台:
/// <summary>
/// C# 14 擴充方法範例
/// </summary>
public static class
ListExtensions {
// 靜態擴充區塊:僅指定接收類型
(IList<T>),沒有參數名稱,泛型型別參數(Generic Type Parameter)名稱為 T
extension<T>(IList<T> lists) {
// 靜態擴充方法:將字串轉為大寫並列印
public void Print() =>
lists?.ToList().ForEach(s => Console.WriteLine(s?.ToString()?.ToUpper()));
}
}
class
Program {
static void Main() {
//泛型型別參數(Generic
Type Parameter)為 string
List<string> messages = new List<string> { "Hello", "World" };
//使用擴充方法
messages.Print();
//也可以直接呼叫靜態擴充方法
ListExtensions.Print(messages);
//泛型型別參數(Generic
Type Parameter)為 int
List<int> messages2 = new List<int> { 100, 200 };
//使用擴充方法
messages2.Print();
//也可以直接呼叫靜態擴充方法
ListExtensions.Print(messages2);
}
}
這個範例執行的結果請參考下圖所示:
圖 5:C# 14版具備泛型型別參數(Generic Type Parameter)的擴充方法
C#14版擴充屬性
了解完擴充方法語法之後,我們來談談擴充屬性(Extension Properties)到底是什麼!擴充屬性可以是實例擴充屬性
(Instance Extension Properties)或靜態擴充屬性 (Static
Extension Properties)。
實例擴充屬性讓你可以替別的
class 增加「看起來像內建」的屬性,允許你像使用類型本身的成員一樣,直接在類型的實例上存取該屬性,語法為「變數名稱.屬性名稱」。
以下是實例擴充屬性 (Instance Extension Properties)範例程式碼,擴充内建的「string」型別,加上一個「Upper」屬性回傳一個全部轉大寫的字串,存取屬性時,不需要加上小括號,而使用「msg.Upper」語法:
using
System;
using
System.Collections.Generic;
using
System.Linq;
public static class
StringExtensions
{
extension(string s)
{
public void Print() => Console.WriteLine(s.ToUpper());
public string Upper => s.ToUpper();
}
}
class
Program
{
static void Main()
{
string msg = "Hello";
msg.Print();
Console.WriteLine(msg.Upper);
}
}
靜態擴充屬性(Static
Extension Properties)
靜態擴充屬性(Static Extension
Properties)無法存取任何特定的物件資料。靜態擴充屬性是直接擴充型別本身,而不是其實例。在「extension」區塊中,如果只宣告了擴充型別而沒有指定參數名稱(例如 extension<T>(IEnumerable<T>)),則定義的成員必須是靜態的
以下是一個針對「string」型別定義靜態擴充屬性(extension(string))的範例,用於提供一個常用的日期格式。
public static class
StringFormatExtensions {
// 靜態擴充區塊:僅指定擴充型別 (string),沒有參數名稱
// 這表示此區塊內的成員必須是靜態的,並透過
'string.PropertyName' 語法存取
extension(string) {
// 靜態擴充屬性:用於提供一個常用的日期格式
public static string MyDateFormatString
=> "**yyyy-MM-dd**";
}
}
class
Program {
static void Main() {
// 直接存取靜態擴充屬性
Console.WriteLine($"{DateTime.Now.ToString(string.MyDateFormatString)}");
}
}
這個範例執行的結果請參考下圖所示:
圖 6:使用靜態擴充屬性
最後特別注意,擴充屬性不能使用自動實作屬性(Auto-Implemented
Properties)語法,因為它們無法在被擴充的型別建立的實例上擁有狀態或欄位。
總結
本文介紹 C# 14版的核心理念:透過「消除雜訊」來減少樣板程式碼,讓程式碼更貼近業務邏輯,同時提升可讀性與安全性。C# 14版帶來的一系列語法糖,例如「field」關鍵字,不僅讓程式碼更簡潔、更安全,也讓程式碼的意圖更加清晰。
第二個重點是Null 條件指派(?.=),由於「NullReferenceException」是常見錯誤,傳統作法需要使用「if」 判斷物件是否為 null 才能安全指派。C# 14 擴充了「?. 」運算子的能力,讓它能用在指派運算子的左側:當物件不為 null 才會進行屬性設定,並且在 null 狀況下也不會執行右側運算(例如呼叫取得資料的方法),因此程式碼更短、更直覺,也更安全。
最後,介紹「extension」區塊語法帶來的擴充成員革新。過去擴充方法必須放在 static class 中、方法要「static」、第一個參數要加「this」;C# 14版以「extension(型別)」 將擴充成員集中管理,語意更清楚。更重要的是新增擴充屬性(Extension
Properties),可用「obj.Property」的直觀方式擴充既有型別,同時也支援靜態擴充屬性供 「string.PropertyName」使用。擴充屬性不能使用自動屬性,因為無法在被擴充型別的實例上保存狀態。整體而言,C# 14版讓開發者用更少的程式碼寫出更安全、易讀且更現代化的 C#。

0 意見:
張貼留言