2026年3月31日 星期二

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("情境 1employee 不為 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("情境 2employee 不為 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("情境 3employee 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("情境 4employee null - C# 14 ?.= 指派");

        Employee? employee4 = null;

        employee4?.Info = GetEmployeeInfo("?.= 指派 - null");

        Console.WriteLine("如果上方未看到 GetEmployeeInfo 執行訊息,代表 null 狀況下不會執行右側指派。");

 

    }

}


這個範例
執行的結果請參考下圖所示:

4Null 條件運算子 ?.)的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);

    }

}

 

這個範例執行的結果請參考下圖所示:

5C# 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 意見:

張貼留言