C# 中的 record 類型簡介

C# 中的 recordclassstruct,可提供特殊語法與行為來使用資料模型。 record 修飾元會指示編譯器合成對主要角色儲存資料的型別有用的成員。 這些成員包含 ToString() 的多載,以及支援實值相等的成員。

使用 record 的時機

請考慮在下列案例中使用 record 取代 class 或 struct:

  • 您想要定義相依於值相等的資料模型。
  • 您想要定義物件為不可變的類型。

實值相等

針對 record,值相等表示若類型相符且所有屬性與欄位值相符,則 record 類型的兩個變數相等。 針對其他參考型別 (例如 class),相等表示參考相等。 也就是說,若 class 類型的兩個變數參考相同的物件,則兩個變數相等。 判斷兩個記錄執行個體相等的方法與運算子會使用值相等。

並非所有的資料模型都適合使用值相等。 例如,Entity Framework Core 相依於參考相等,以確保其只使用實體類型的一個執行個體來表示概念上的一個實體。 因此,record 類型不適合用作 Entity Framework Core 中的實體類型。

不變性

不可變的類型是一種可讓您在物件具現化之後,變更物件之任何屬性或欄位值的類型。 當您需要類型為安全執行緒,或是您相依於雜湊表中保持不變的雜湊碼時,則不變性可能會很實用。 Record 提供簡潔的語法,以建立及使用不可變的類型。

不變性並不適用於所有資料案例。 例如,Entity Framework Core 不支援使用不可變的實體類型來進行更新。

record 與 class 及 struct 有何不同

宣告 class 或 struct 並將其具現化的相同語法可以搭配 record 一起使用。 只要以 record 取代 class 關鍵字,或使用 record struct,而不是 struct。 同樣地,record 類別也支援表示繼承關聯性的相同語法。 record 與 class 的不同之處有以下幾點:

  • 您可以在主要建構函式中使用位置參數來建立具有不可變屬性的型別,並將其具現化。
  • 在 class 中表示參考相等或不等的相同方法與運算子 (例如 Object.Equals(Object)==),表示 record 中的值相等或不等
  • 您可以使用 with 運算式,在選取的屬性中,以新的值建立不可變物件的複本。
  • record 的 ToString 方法會建立格式化字串,其顯示物件的類型名稱與其所有公用屬性的名稱與值。
  • record 可以繼承自另一個 record。 record 無法繼承自 class,而 class 也無法繼承自 record。

Record struct 與 struct 不同,因為編譯器會合成相等的方法,以及 ToString。 編譯器會合成位置 record struct 的 Deconstruct 方法。

編譯器會為 record class 中的每個主要建構函式參數合成公用的僅限 init 屬性。 在 record struct 中,編譯器會合成公用讀寫屬性。 編譯器不會為 class 中的主要建構函式參數以及不包含 record 修飾元的 struct 型別建立屬性。

範例

下列範例會定義使用位置參數宣告 record 的 public record,並將其具現化。 然後,其會列印類型名稱與屬性值:


public record Person(string FirstName, string LastName);

public static class Program
{
    public static void Main()
    {
        Person person = new("Nancy", "Davolio");
        Console.WriteLine(person);
        // output: Person { FirstName = Nancy, LastName = Davolio }
    }

}

下列範例示範 record 中的值相等:

public record Person(string FirstName, string LastName, string[] PhoneNumbers);
public static class Program
{
    public static void Main()
    {
        var phoneNumbers = new string[2];
        Person person1 = new("Nancy", "Davolio", phoneNumbers);
        Person person2 = new("Nancy", "Davolio", phoneNumbers);
        Console.WriteLine(person1 == person2); // output: True

        person1.PhoneNumbers[0] = "555-1234";
        Console.WriteLine(person1 == person2); // output: True

        Console.WriteLine(ReferenceEquals(person1, person2)); // output: False
    }
}

下列範例示範如何使用 with 運算式複製不可變的物件,並變更其中一個屬性:

public record Person(string FirstName, string LastName)
{
    public required string[] PhoneNumbers { get; init; }
}

public class Program
{
    public static void Main()
    {
        Person person1 = new("Nancy", "Davolio") { PhoneNumbers = new string[1] };
        Console.WriteLine(person1);
        // output: Person { FirstName = Nancy, LastName = Davolio, PhoneNumbers = System.String[] }

        Person person2 = person1 with { FirstName = "John" };
        Console.WriteLine(person2);
        // output: Person { FirstName = John, LastName = Davolio, PhoneNumbers = System.String[] }
        Console.WriteLine(person1 == person2); // output: False

        person2 = person1 with { PhoneNumbers = new string[1] };
        Console.WriteLine(person2);
        // output: Person { FirstName = Nancy, LastName = Davolio, PhoneNumbers = System.String[] }
        Console.WriteLine(person1 == person2); // output: False

        person2 = person1 with { };
        Console.WriteLine(person1 == person2); // output: True
    }
}

如需詳細資訊,請參閱 record (C# 參考)

C# 語言規格

如需詳細資訊,請參閱<C# 語言規格>。 語言規格是 C# 語法及用法的限定來源。