Rekordy (odwołanie w C#)

Modyfikator służy record do definiowania typu odwołania, który zapewnia wbudowane funkcje hermetyzacji danych. Język C# 10 umożliwia składnię record class jako synonim, aby wyjaśnić typ odwołania i record struct zdefiniować typ wartości z podobną funkcjonalnością.

Podczas deklarowania podstawowego konstruktora w rekordzie kompilator generuje właściwości publiczne dla podstawowych parametrów konstruktora. Podstawowe parametry konstruktora do rekordu są określane jako parametry pozycyjne. Kompilator tworzy właściwości pozycyjne, które odzwierciedlają podstawowy konstruktor lub parametry pozycyjne. Kompilator nie syntetyzuje właściwości podstawowych parametrów konstruktora w typach, które nie mają record modyfikatora.

W poniższych dwóch przykładach pokazano record (lub record class) typy referencyjne:

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

W poniższych dwóch przykładach przedstawiono record struct typy wartości:

public readonly record struct Point(double X, double Y, double Z);
public record struct Point
{
    public double X { get; init; }
    public double Y { get; init; }
    public double Z { get; init; }
}

Można również tworzyć rekordy z modyfikowalnymi właściwościami i polami:

public record Person
{
    public required string FirstName { get; set; }
    public required string LastName { get; set; }
};

Struktury rekordów mogą być również modyfikowalne, zarówno struktury rekordów pozycyjnych, jak i struktury rekordów bez parametrów pozycyjnych:

public record struct DataMeasurement(DateTime TakenAt, double Measurement);
public record struct Point
{
    public double X { get; set; }
    public double Y { get; set; }
    public double Z { get; set; }
}

Rekordy mogą być modyfikowalne, ale są przeznaczone głównie do obsługi niezmiennych modeli danych. Typ rekordu oferuje następujące funkcje:

W powyższych przykładach pokazano pewne różnice między rekordami, które są typami referencyjnymi i rekordami, które są typami wartości:

  • A record lub record class deklaruje typ odwołania. Słowo class kluczowe jest opcjonalne, ale może zwiększyć czytelność dla czytelników. A record struct deklaruje typ wartości.
  • Właściwości pozycyjne są niezmienne w obiekcie record class i readonly record struct. Są one modyfikowalne w obiekcie record struct.

W pozostałej części tego artykułu omówiono zarówno typy, jak record class i record struct . Różnice są szczegółowo opisane w każdej sekcji. Należy zdecydować między elementem a record classrecord struct podobnym do podejmowania decyzji między elementem a class a struct. Rekord terminu służy do opisywania zachowania, które ma zastosowanie do wszystkich typów rekordów. Albo record struct lub record class służy do opisywania zachowania, które dotyczy tylko typów struktur lub klas, odpowiednio. Typ record struct został wprowadzony w języku C# 10.

Składnia pozycyjna definicji właściwości

Parametry pozycyjne umożliwiają deklarowanie właściwości rekordu i inicjowanie wartości właściwości podczas tworzenia wystąpienia:

public record Person(string FirstName, string LastName);

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

Gdy używasz składni pozycyjnej dla definicji właściwości, kompilator tworzy:

  • Publiczna właściwość autoimplementowana dla każdego parametru pozycyjnego podanego w deklaracji rekordu.
    • W przypadku record typów i readonly record struct typów: właściwość tylko do inicjowania.
    • W przypadku record struct typów: właściwość read-write.
  • Podstawowy konstruktor, którego parametry pasują do parametrów pozycyjnych w deklaracji rekordu.
  • W przypadku typów struktur rekordów konstruktor bez parametrów, który ustawia każde pole na wartość domyślną.
  • Deconstruct Metoda z parametrem out dla każdego parametru pozycyjnego podanego w deklaracji rekordu. Właściwości dekonstrukcji metody zdefiniowane przy użyciu składni pozycyjnej; Ignoruje właściwości zdefiniowane przy użyciu standardowej składni właściwości.

Możesz dodać atrybuty do dowolnego z tych elementów, które kompilator tworzy na podstawie definicji rekordu. Element docelowy można dodać do dowolnego atrybutu, który ma zastosowanie do właściwości rekordu pozycyjnego. Poniższy przykład dotyczy System.Text.Json.Serialization.JsonPropertyNameAttribute każdej właściwości rekordu Person . Element property: docelowy wskazuje, że atrybut jest stosowany do właściwości wygenerowanej przez kompilator. Inne wartości to field: zastosowanie atrybutu do pola i param: zastosowanie atrybutu do parametru .

/// <summary>
/// Person record type
/// </summary>
/// <param name="FirstName">First Name</param>
/// <param name="LastName">Last Name</param>
/// <remarks>
/// The person type is a positional record containing the
/// properties for the first and last name. Those properties
/// map to the JSON elements "firstName" and "lastName" when
/// serialized or deserialized.
/// </remarks>
public record Person([property: JsonPropertyName("firstName")] string FirstName, 
    [property: JsonPropertyName("lastName")] string LastName);

W poprzednim przykładzie pokazano również, jak utworzyć komentarze dokumentacji XML dla rekordu. Możesz dodać tag , aby dodać dokumentację <param> parametrów podstawowego konstruktora.

Jeśli wygenerowana automatycznie zaimplementowana definicja właściwości nie jest odpowiednia, możesz zdefiniować własną właściwość o tej samej nazwie. Na przykład możesz zmienić dostępność lub możliwość mutowania albo zapewnić implementację metody get lub set metody dostępu. Jeśli zadeklarujesz właściwość w źródle, musisz zainicjować ją z parametru pozycyjnego rekordu. Jeśli właściwość jest właściwością automatycznie zaimplementowaną, musisz zainicjować właściwość . Jeśli dodasz pole kopii zapasowej w źródle, musisz zainicjować pole zapasowe. Wygenerowany dekonstruktor używa definicji właściwości. Na przykład poniższy przykład deklaruje FirstName właściwości i LastName rekordu publicpozycyjnego, ale ogranicza parametr pozycyjny Id do internal. Tej składni można używać dla rekordów i typów struktur rekordów.

public record Person(string FirstName, string LastName, string Id)
{
    internal string Id { get; init; } = Id;
}

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

}

Typ rekordu nie musi deklarować żadnych właściwości pozycyjnych. Rekord można zadeklarować bez żadnych właściwości pozycyjnych i zadeklarować inne pola i właściwości, jak w poniższym przykładzie:

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

Jeśli definiujesz właściwości przy użyciu standardowej składni właściwości, ale pomijasz modyfikator dostępu, właściwości są niejawnie private.

Niezmienność

Rekord pozycyjny i pozycyjna struktura rekordu odczytu deklarują właściwości tylko do inicjowania. Struktura rekordów pozycyjnych deklaruje właściwości odczytu i zapisu. Możesz zastąpić jedną z tych wartości domyślnych, jak pokazano w poprzedniej sekcji.

Niezmienność może być przydatna, gdy potrzebujesz typu skoncentrowanego na danych, aby był bezpieczny wątkowo lub zależysz od kodu skrótu pozostałego w tabeli skrótu. Niezmienność nie jest jednak odpowiednia dla wszystkich scenariuszy danych. Na przykład program Entity Framework Core nie obsługuje aktualizacji przy użyciu niezmiennych typów jednostek.

Właściwości tylko do inicjowania, niezależnie od tego, czy zostały utworzone na podstawie parametrów pozycyjnych (record classi readonly record struct) lub przez określenie init metod dostępu, mają płytkią niezmienność. Po zainicjowaniu nie można zmienić wartości właściwości typu wartości ani odwołania do właściwości typu odwołania. Można jednak zmienić dane, do których odwołuje się właściwość typu odwołania. W poniższym przykładzie pokazano, że zawartość niezmiennej właściwości typu odwołania (tablica w tym przypadku) jest modyfikowalna:

public record Person(string FirstName, string LastName, string[] PhoneNumbers);

public static void Main()
{
    Person person = new("Nancy", "Davolio", new string[1] { "555-1234" });
    Console.WriteLine(person.PhoneNumbers[0]); // output: 555-1234

    person.PhoneNumbers[0] = "555-6789";
    Console.WriteLine(person.PhoneNumbers[0]); // output: 555-6789
}

Funkcje unikatowe dla typów rekordów są implementowane przez metody syntetyzowane kompilatora, a żadna z tych metod nie narusza niezmienności przez modyfikowanie stanu obiektu. O ile nie określono, syntetyzowane metody są generowane dla recorddeklaracji , record structi readonly record struct .

Równość wartości

Jeśli metody równości nie są zastępowane lub zastępowane, zadeklarowany typ określa sposób definiowania równości:

  • W przypadku class typów dwa obiekty są równe, jeśli odwołują się do tego samego obiektu w pamięci.
  • W przypadku struct typów dwa obiekty są równe, jeśli są tego samego typu i przechowują te same wartości.
  • W przypadku typów z record modyfikatorem (record class, , i readonly record struct) dwa obiekty są równe, record structjeśli są tego samego typu i przechowują te same wartości.

Definicja równości elementu record struct jest taka sama jak w przypadku elementu struct. Różnica polega na tym, że w przypadku structelementu implementacja jest włączona ValueType.Equals(Object) i opiera się na odbiciu. W przypadku rekordów implementacja jest syntetyzowana i używa zadeklarowanych elementów członkowskich danych.

Równość odwołań jest wymagana dla niektórych modeli danych. Na przykład platforma Entity Framework Core zależy od równości referencyjnej, aby upewnić się, że używa tylko jednego wystąpienia typu jednostki dla tego, co jest koncepcyjnie jedną jednostką. Z tego powodu rekordy i struktury rekordów nie są odpowiednie do użycia jako typy jednostek w programie Entity Framework Core.

W poniższym przykładzie przedstawiono równość wartości typów rekordów:

public record Person(string FirstName, string LastName, string[] PhoneNumbers);

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
}

Aby zaimplementować równość wartości, kompilator syntetyzuje kilka metod, w tym:

  • Przesłonięcia elementu Object.Equals(Object). Jest to błąd, jeśli zastąpienie jest zadeklarowane jawnie.

    Ta metoda jest używana jako podstawa metody statycznej Object.Equals(Object, Object) , gdy oba parametry są inne niż null.

  • A virtual, lub sealed, Equals(R? other) gdzie R jest typem rekordu. Ta metoda implementuje metodę IEquatable<T>. Tę metodę można zadeklarować jawnie.

  • Jeśli typ rekordu pochodzi z typu Baserekordu podstawowego , Equals(Base? other). Jest to błąd, jeśli zastąpienie jest zadeklarowane jawnie. Jeśli udostępnisz własną implementację Equals(R? other)programu , podaj również implementację GetHashCode .

  • Przesłonięcia elementu Object.GetHashCode(). Tę metodę można zadeklarować jawnie.

  • Przesłonięcia operatorów == i !=. Jest to błąd, jeśli operatory są zadeklarowane jawnie.

  • Jeśli typ rekordu pochodzi z typu rekordu podstawowego, protected override Type EqualityContract { get; };. Tę właściwość można zadeklarować jawnie. Aby uzyskać więcej informacji, zobacz Równość w hierarchiach dziedziczenia.

Kompilator nie syntetyzuje metody, gdy typ rekordu ma metodę zgodną z podpisem metody syntetyzowanej, którą można jawnie zadeklarować.

Mutacja niestrukturalna

Jeśli musisz skopiować wystąpienie z pewnymi modyfikacjami, możesz użyć with wyrażenia w celu osiągnięcia niezniszczanej mutacji. Wyrażenie with tworzy nowe wystąpienie rekordu, które jest kopią istniejącego wystąpienia rekordu, z określonymi właściwościami i polami zmodyfikowanymi. Składnia inicjatora obiektów służy do określania wartości, które mają zostać zmienione, jak pokazano w poniższym przykładzie:

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

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
}

Wyrażenie with może ustawiać właściwości pozycyjne lub właściwości utworzone przy użyciu standardowej składni właściwości. Jawnie zadeklarowane właściwości muszą mieć element init lub set metodę dostępu do zmiany w wyrażeniu with .

Wynik with wyrażenia jest płytkią kopią, co oznacza, że dla właściwości odwołania jest kopiowane tylko odwołanie do wystąpienia. Zarówno oryginalny rekord, jak i kopia kończą się odwołaniem do tego samego wystąpienia.

Aby zaimplementować tę funkcję dla record class typów, kompilator syntetyzuje metodę klonowania i konstruktor kopiujący. Metoda klonowania wirtualnego zwraca nowy rekord zainicjowany przez konstruktor kopiowania. Gdy używasz with wyrażenia, kompilator tworzy kod, który wywołuje metodę clone, a następnie ustawia właściwości określone w wyrażeniu with .

Jeśli potrzebujesz innego zachowania kopiowania, możesz napisać własny konstruktor kopiowania w obiekcie record class. Jeśli to zrobisz, kompilator nie zsyntetyzuje go. Utwórz konstruktor private , jeśli rekord ma sealedwartość , w przeciwnym razie ustaw go protectedna . Kompilator nie syntetyzuje konstruktora kopiującego dla record struct typów. Można go napisać, ale kompilator nie generuje wywołań do niego dla with wyrażeń. Wartości elementu record struct są kopiowane podczas przypisywania.

Nie można zastąpić metody klonowania i nie można utworzyć elementu członkowskiego o nazwie Clone w żadnym typie rekordu. Rzeczywista nazwa metody klonowania jest generowana przez kompilator.

Wbudowane formatowanie na potrzeby wyświetlania

Typy rekordów mają metodę wygenerowaną ToString przez kompilator, która wyświetla nazwy i wartości właściwości publicznych i pól. Metoda ToString zwraca ciąg następującego formatu:

<nazwa typu rekordu { <nazwa>> właściwości = <wartość>, <nazwa> właściwości = <wartość>, ...}

Ciąg drukowany <value> jest ciągiem zwracanym przez ToString() typ właściwości . W poniższym przykładzie ChildNames jest to System.Array, gdzie ToString zwraca wartość System.String[]:

Person { FirstName = Nancy, LastName = Davolio, ChildNames = System.String[] }

Aby zaimplementować tę funkcję, w record class typach kompilator syntetyzuje metodę wirtualną PrintMembers i przesłonięć ToString . W record struct typach ten element członkowski to private. Zastąp ToString tworzy StringBuilder obiekt o nazwie typu, po którym następuje nawias otwierający. Wywołuje PrintMembers metodę dodawania nazw i wartości właściwości, a następnie dodaje nawias zamykający. W poniższym przykładzie pokazano kod podobny do tego, co zawiera syntetyzowane przesłonięcia:

public override string ToString()
{
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.Append("Teacher"); // type name
    stringBuilder.Append(" { ");
    if (PrintMembers(stringBuilder))
    {
        stringBuilder.Append(" ");
    }
    stringBuilder.Append("}");
    return stringBuilder.ToString();
}

Możesz podać własną implementację PrintMembers lub przesłonięć ToString . Przykłady znajdują się w PrintMembers sekcji formatowania w sekcji rekordów pochodnych w dalszej części tego artykułu. W języku C# 10 lub nowszym implementacja ToString programu może obejmować sealed modyfikator, który uniemożliwia kompilatorowi synchronizowanie ToString implementacji dla dowolnych rekordów pochodnych. Możesz utworzyć spójną reprezentację ciągu w całej record hierarchii typów. (Rekordy pochodne nadal mają metodę wygenerowaną PrintMembers dla wszystkich właściwości pochodnych).

Dziedziczenie

Ta sekcja dotyczy record class tylko typów.

Rekord może dziedziczyć z innego rekordu. Jednak rekord nie może dziedziczyć z klasy, a klasa nie może dziedziczyć z rekordu.

Parametry pozycyjne w typach rekordów pochodnych

Rekord pochodny deklaruje parametry pozycyjne dla wszystkich parametrów w podstawowym konstruktorze rekordu podstawowego. Rekord podstawowy deklaruje i inicjuje te właściwości. Rekord pochodny nie ukrywa ich, ale tworzy i inicjuje właściwości parametrów, które nie są deklarowane w rekordzie podstawowym.

Poniższy przykład ilustruje dziedziczenie ze składnią właściwości pozycyjnej:

public abstract record Person(string FirstName, string LastName);
public record Teacher(string FirstName, string LastName, int Grade)
    : Person(FirstName, LastName);
public static void Main()
{
    Person teacher = new Teacher("Nancy", "Davolio", 3);
    Console.WriteLine(teacher);
    // output: Teacher { FirstName = Nancy, LastName = Davolio, Grade = 3 }
}

Równość w hierarchiach dziedziczenia

Ta sekcja dotyczy record class typów, ale nie record struct typów. Dla dwóch zmiennych rekordów, które mają być równe, typ czasu wykonywania musi być równy. Typy zawierających zmienne mogą być różne. Porównanie dziedziczonej równości przedstawiono w poniższym przykładzie kodu:

public abstract record Person(string FirstName, string LastName);
public record Teacher(string FirstName, string LastName, int Grade)
    : Person(FirstName, LastName);
public record Student(string FirstName, string LastName, int Grade)
    : Person(FirstName, LastName);
public static void Main()
{
    Person teacher = new Teacher("Nancy", "Davolio", 3);
    Person student = new Student("Nancy", "Davolio", 3);
    Console.WriteLine(teacher == student); // output: False

    Student student2 = new Student("Nancy", "Davolio", 3);
    Console.WriteLine(student2 == student); // output: True
}

W tym przykładzie wszystkie zmienne są deklarowane jako Person, nawet jeśli wystąpienie jest typem Student pochodnym typu lub Teacher. Wystąpienia mają te same właściwości i te same wartości właściwości. Jednak student == teacher zwraca wartość False , chociaż obie są Personzmiennymi typu i student == student2 zwracane True , chociaż jedna jest zmienną Person , a jedna jest zmienną Student . Test równości zależy od typu środowiska uruchomieniowego rzeczywistego obiektu, a nie zadeklarowanego typu zmiennej.

Aby zaimplementować to zachowanie, kompilator syntetyzuje właściwość zwracającą EqualityContractType obiekt zgodny z typem rekordu. Dzięki EqualityContract metodom równości można porównać typ środowiska uruchomieniowego obiektów podczas sprawdzania równości. Jeśli podstawowym typem rekordu jest object, ta właściwość to virtual. Jeśli typ podstawowy jest innym typem rekordu, ta właściwość jest przesłonięć. Jeśli typ rekordu to sealed, ta właściwość jest efektywna sealed , ponieważ typem jest sealed.

Gdy kod porównuje dwa wystąpienia typu pochodnego, syntetyzowane metody równości sprawdzają wszystkie składowe danych podstawowych i pochodnych typów pod kątem równości. Metoda syntetyzowana GetHashCode używa GetHashCode metody ze wszystkich składowych danych zadeklarowanych w typie podstawowym i typie rekordu pochodnego. Składowe record danych obiektu obejmują wszystkie zadeklarowane pola i pole tworzenia kopii zapasowej syntetyzowane przez kompilator dla wszystkich automatycznie zaimplementowanych właściwości.

with wyrażenia w rekordach pochodnych

Wynik with wyrażenia ma ten sam typ czasu wykonywania co operand wyrażenia. Wszystkie właściwości typu czasu wykonywania są kopiowane, ale można ustawić tylko właściwości typu czasu kompilacji, jak pokazano w poniższym przykładzie:

public record Point(int X, int Y)
{
    public int Zbase { get; set; }
};
public record NamedPoint(string Name, int X, int Y) : Point(X, Y)
{
    public int Zderived { get; set; }
};

public static void Main()
{
    Point p1 = new NamedPoint("A", 1, 2) { Zbase = 3, Zderived = 4 };

    Point p2 = p1 with { X = 5, Y = 6, Zbase = 7 }; // Can't set Name or Zderived
    Console.WriteLine(p2 is NamedPoint);  // output: True
    Console.WriteLine(p2);
    // output: NamedPoint { X = 5, Y = 6, Zbase = 7, Name = A, Zderived = 4 }

    Point p3 = (NamedPoint)p1 with { Name = "B", X = 5, Y = 6, Zbase = 7, Zderived = 8 };
    Console.WriteLine(p3);
    // output: NamedPoint { X = 5, Y = 6, Zbase = 7, Name = B, Zderived = 8 }
}

PrintMembers formatowanie w rekordach pochodnych

Metoda syntetyzowana PrintMembers typu rekordu pochodnego wywołuje implementację podstawową. Wynikiem jest to, że wszystkie właściwości publiczne i pola obu typów pochodnych i podstawowych są uwzględnione w ToString danych wyjściowych, jak pokazano w poniższym przykładzie:

public abstract record Person(string FirstName, string LastName);
public record Teacher(string FirstName, string LastName, int Grade)
    : Person(FirstName, LastName);
public record Student(string FirstName, string LastName, int Grade)
    : Person(FirstName, LastName);

public static void Main()
{
    Person teacher = new Teacher("Nancy", "Davolio", 3);
    Console.WriteLine(teacher);
    // output: Teacher { FirstName = Nancy, LastName = Davolio, Grade = 3 }
}

Możesz podać własną implementację PrintMembers metody . Jeśli to zrobisz, użyj następującego podpisu:

  • Dla rekordu sealed pochodzącego z object (nie deklaruje rekordu podstawowego): private bool PrintMembers(StringBuilder builder);
  • W przypadku rekordu sealed pochodzącego z innego rekordu (należy pamiętać, że typ otaczający to sealed, więc metoda jest skutecznie sealed): protected override bool PrintMembers(StringBuilder builder);
  • Dla rekordu, który nie sealed jest i pochodzi z obiektu: protected virtual bool PrintMembers(StringBuilder builder);
  • Dla rekordu, który nie sealed jest i pochodzi z innego rekordu: protected override bool PrintMembers(StringBuilder builder);

Oto przykład kodu, który zastępuje syntetyzowane PrintMembers metody, jeden dla typu rekordu, który pochodzi z obiektu, i jeden dla typu rekordu, który pochodzi z innego rekordu:

public abstract record Person(string FirstName, string LastName, string[] PhoneNumbers)
{
    protected virtual bool PrintMembers(StringBuilder stringBuilder)
    {
        stringBuilder.Append($"FirstName = {FirstName}, LastName = {LastName}, ");
        stringBuilder.Append($"PhoneNumber1 = {PhoneNumbers[0]}, PhoneNumber2 = {PhoneNumbers[1]}");
        return true;
    }
}

public record Teacher(string FirstName, string LastName, string[] PhoneNumbers, int Grade)
    : Person(FirstName, LastName, PhoneNumbers)
{
    protected override bool PrintMembers(StringBuilder stringBuilder)
    {
        if (base.PrintMembers(stringBuilder))
        {
            stringBuilder.Append(", ");
        };
        stringBuilder.Append($"Grade = {Grade}");
        return true;
    }
};

public static void Main()
{
    Person teacher = new Teacher("Nancy", "Davolio", new string[2] { "555-1234", "555-6789" }, 3);
    Console.WriteLine(teacher);
    // output: Teacher { FirstName = Nancy, LastName = Davolio, PhoneNumber1 = 555-1234, PhoneNumber2 = 555-6789, Grade = 3 }
}

Uwaga

W języku C# 10 lub nowszym kompilator zsyntetyzuje PrintMembers w rekordach pochodnych nawet wtedy, gdy rekord podstawowy zapieczętował metodę ToString . Możesz również utworzyć własną implementację elementu PrintMembers.

Zachowanie dekonstruktora w rekordach pochodnych

Metoda Deconstruct rekordu pochodnego zwraca wartości wszystkich właściwości pozycyjnych typu czasu kompilacji. Jeśli typ zmiennej jest rekordem podstawowym, tylko właściwości rekordu podstawowego są dekonstrukcyjne, chyba że obiekt jest rzutowany na typ pochodny. W poniższym przykładzie pokazano wywołanie dekonstruktora dla rekordu pochodnego.

public abstract record Person(string FirstName, string LastName);
public record Teacher(string FirstName, string LastName, int Grade)
    : Person(FirstName, LastName);
public record Student(string FirstName, string LastName, int Grade)
    : Person(FirstName, LastName);

public static void Main()
{
    Person teacher = new Teacher("Nancy", "Davolio", 3);
    var (firstName, lastName) = teacher; // Doesn't deconstruct Grade
    Console.WriteLine($"{firstName}, {lastName}");// output: Nancy, Davolio

    var (fName, lName, grade) = (Teacher)teacher;
    Console.WriteLine($"{fName}, {lName}, {grade}");// output: Nancy, Davolio, 3
}

Ograniczenia ogólne

Słowo record kluczowe jest modyfikatorem typu class lub struct . record Dodanie modyfikatora obejmuje zachowanie opisane wcześniej w tym artykule. Nie ma żadnego ogólnego ograniczenia, które wymaga, aby typ był rekordem. A record class spełnia class ograniczenie. A record struct spełnia struct ograniczenie. Aby uzyskać więcej informacji, zobacz Ograniczenia dotyczące parametrów typu.

specyfikacja języka C#

Aby uzyskać więcej informacji, zobacz sekcję Klasy specyfikacji języka C#.

Aby uzyskać więcej informacji na temat tych funkcji, zobacz następujące uwagi dotyczące propozycji funkcji:

Zobacz też