Einführung in Datensatztypen in C#

Bei einemDatensatz in C# handelt es sich um eine Klasse oder Struktur, die eine besondere Syntax und ein besonderes Verhalten für den Umgang mit Datenmodellen aufweist. Der record-Modifizierer weist den Compiler an, Member zu synthetisieren, die für Typen nützlich sind, deren primäre Rolle das Speichern von Daten ist. Diese Member enthalten eine Überladung von ToString() und Member, die Wertgleichheit unterstützen.

Verwendungszwecke von Datensätzen

Sie sollten die Verwendung eines Datensatzes anstelle einer Klasse oder Struktur in folgenden Szenarien in Erwägung ziehen:

  • Sie möchten ein Datenmodell definieren, das von der Wertgleichheit abhängt.
  • Sie möchten einen Typ definieren, dessen Objekte unveränderlich sind.

Wertgleichheit

Bei Datensätzen bedeutet Wertgleichheit, dass zwei Variablen eines Datensatztyps gleich sind, wenn die Typen sowie alle Eigenschafts- und Feldwerte übereinstimmen. Bei anderen Verweistypen wie Klassen ist mit Gleichheit die Verweisgleichheit gemeint. Zwei Variablen eines Klassentyps sind demnach gleich, wenn sie auf dasselbe Objekt verweisen. Methoden und Operatoren, die die Gleichheit zweier Datensatzinstanzen bestimmen, verwenden die Wertgleichheit.

Nicht alle Datenmodelle funktionieren gut mit der Wertgleichheit. Entity Framework Core hängt beispielsweise von der Verweisgleichheit ab, um sicherzustellen, dass für konzeptionell eine Entität nur eine Instanz eines Entitätstyps verwendet wird. Aus diesem Grund sind Datensatztypen für die Verwendung als Entitätstypen in Entity Framework Core nicht geeignet.

Unveränderlichkeit

Ein unveränderlicher Typ verhindert, dass Sie Eigenschafts- oder Feldwerte eines Objekts ändern, nachdem es instanziiert wurde. Die Unveränderlichkeit kann hilfreich sein, wenn Sie einen threadsicheren Typ benötigen oder davon abhängig sind, dass ein Hashcode in einer Hashtabelle unverändert bleibt. Datensätze weisen eine präzise Syntax zum Erstellen von und Arbeiten mit unveränderlichen Typen auf.

Die Unveränderlichkeit ist nicht für alle Datenszenarios geeignet. Entity Framework Core unterstützt beispielsweise keine Updates mit unveränderlichen Entitätstypen.

Unterschiede zwischen Datensätzen und Klassen sowie Strukturen

Die Syntax, mit der Klassen oder Strukturen deklariert und instanziiert werden, kann auch für Datensätze verwendet werden. Ersetzen Sie einfach das class-Schlüsselwort durch record, oder verwenden Sie record struct anstelle von struct. Ebenso unterstützen Datensatzklassen die Syntax zum Ausdrücken von Vererbungsbeziehungen. Datensätze unterscheiden sich folgendermaßen von Klassen:

  • Sie können Positionsparameter in einem primären Konstruktor verwenden, um einen Typ mit unveränderlichen Eigenschaften zu erstellen und zu instanziieren.
  • Die Methoden und Operatoren, die bei Klassen die Verweisgleichheit oder -ungleichheit angeben (z. B. Object.Equals(Object) und ==), geben bei Datensätzen die Wertgleichheit oder -ungleichheit an.
  • Sie können einen with-Ausdruck verwenden, um eine Kopie eines unveränderlichen Objekts mit neuen Werten für ausgewählte Eigenschaften zu erstellen.
  • Die ToString-Methode eines Datensatzes erstellt eine formatierte Zeichenfolge, die den Namen eines Objekttyps sowie die Namen und Werte all seiner öffentlichen Eigenschaften anzeigt.
  • Ein Datensatz kann von einem anderen Datensatz erben. Ein Datensatz kann nicht von einer Klasse erben, und eine Klasse kann nicht von einem Datensatz erben.

Datensatzstrukturen unterscheiden sich von Strukturen darin, dass der Compiler die Methoden für Gleichheit und ToString synthetisiert. Der Compiler synthetisiert eine Deconstruct-Methode für positionelle Datensatzstrukturen.

Der Compiler synthetisiert eine öffentliche Eigenschaft nur für Initialisierung für jeden Parameter eines primären Konstruktors in einer record class. In einer record struct synthetisiert der Compiler eine öffentliche Lese-/Schreibeigenschaft. Der Compiler erstellt keine Eigenschaften für Parameter von primären Konstruktoren in class- und struct-Typen, die keinen record-Modifizierer enthalten.

Beispiele

Im folgenden Beispiel wird ein öffentlicher Datensatz definiert, der positionelle Parameter zum Deklarieren und Instanziieren eines Datensatzes verwendet. Anschließend werden der Typname und die Eigenschaftswerte ausgegeben:


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 }
    }

}

Im folgenden Beispiel wird die Wertgleichheit bei Datensätzen veranschaulicht:

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
    }
}

Im folgenden Beispiel wird die Verwendung eines with-Ausdrucks zum Kopieren eines unveränderlichen Objekts und zum Ändern einer der Eigenschaften veranschaulicht:

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
    }
}

Weitere Informationen finden Sie unter Datensätze (C#-Referenz).

C#-Programmiersprachenspezifikation

Weitere Informationen erhalten Sie unter C#-Sprachspezifikation. Die Sprachspezifikation ist die verbindliche Quelle für die Syntax und Verwendung von C#.