CodeGym /Kurse /C# SELF /record mit explizitem...

record mit explizitem Body und record struct

C# SELF
Level 19 , Lektion 3
Verfügbar

1. Einführung

Der positionsbasierte Syntax eines record-Klassen ist echt praktisch für einfache Fälle:

public record User(string Name, int Age);
Positionsbasierte record-Klasse

Aber manchmal hast du einfach Bock, dem record noch eigene Methoden, spezielle Properties, andere Zugriffsmodifikatoren oder Logik im Konstruktor (z.B. Validierung oder automatische Datenumwandlung) zu verpassen. Leider gibt’s dafür im positionsbasierten Syntax keinen Platz! Zeit für den expliziten Body, wenn:

  • Du willst den record mit Methoden, Properties oder eigener Logik erweitern.
  • Du willst das Verhalten der Properties kontrollieren (Setter, Getter, inits, Validierung).
  • Du brauchst mehrere verschiedene Konstruktoren für verschiedene Initialisierungsarten.
  • Du willst Interfaces implementieren oder spezielle Methoden einbauen.

Bauen wir einen record mit Body

Der Syntax sieht fast aus wie bei einer Klasse. Kommt dir das bekannt vor?


public class User
{
    /* ... */
}

Jetzt ist es aber ein record:


public record User
{
    // Explizit definierte Properties
    public string Name { get; init; }
    public int Age { get; init; }

    // Zusätzliche Logik
    public string GetGreeting()
    {
        return $"Hallo, ich heiße {Name} und bin {Age} Jahre alt!";
    }

    // Custom-Konstruktor
    public User(string name, int age)
    {
        Name = name;
        if (age < 0)
            throw new ArgumentException("Alter darf nicht negativ sein!");
        Age = age;
    }
}

Fun Fact: Wenn du deine Properties explizit definierst, erstellt der Compiler keine automatischen Properties aus dem positionsbasierten Syntax. Alles explizit, alles unter Kontrolle.

Gemischte Variante: Hybrid-Syntax

C# erlaubt dir, beide Welten zu kombinieren: Du kannst einen positionsbasierten record deklarieren und trotzdem einen Body hinzufügen:


public record User(string Name, int Age)
{
    public string GetGreeting()
    {
        return $"Ich bin {Name}, {Age} Jahre alt!";
    }
}
Positionsbasierter record mit Body und zusätzlichen Methoden

In diesem Fall werden die Properties Name und Age weiterhin automatisch durch den positionsbasierten Syntax erstellt, und deine zusätzlichen Methoden wohnen gemütlich im Body.

2. Unterschiede zu normalen records – Body-Details

  • Ein expliziter record gibt dir volle Kontrolle über Konstruktoren, Properties, Methoden.
  • Du kannst Interfaces implementieren oder eigene Vergleichslogik einbauen, wenn du komplexe Regeln für die Objektidentifikation brauchst.
  • Im Gegensatz zum einfachen positionsbasierten record kannst du neue Properties einfach im Body deklarieren.

// Anfängerfehler!
public record User(string Name, int Age)
{
    public string Name { get; init; } // ← Konflikt! Property doppelt deklariert
}

Anfängerfehler: Manche Studis versuchen, gleichzeitig public record User(string Name, int Age) zu schreiben und im Body noch public string Name { get; init; } hinzuzufügen, weil sie denken, das wären zwei verschiedene Variablen. Nope! Das gibt einen Konflikt (doppelte Deklaration). Entweder komplett positionsbasiert oder alles explizit – nicht mischen.

Praktische Beispiele

Wir entwickeln weiter unsere Konsolen-App, in der der User Bestellungen anlegt. Angenommen, es gibt die Klasse Order, die bisher so aussah:

public record Order(string Product, int Quantity, double Price);

Nehmen wir an, wir brauchen jetzt eine Validierung für die Menge (quantity darf nicht kleiner als 1 sein) und eine zusätzliche Property für die Gesamtkosten:


public record Order
{
    public string Product { get; init; }
    public int Quantity { get; init; }
    public double Price { get; init; }
    public double TotalCost => Quantity * Price;

    public Order(string product, int quantity, double price)
    {
        Product = product ?? throw new ArgumentNullException(nameof(product));
        if (quantity < 1)
            throw new ArgumentException("Menge muss mindestens 1 sein!");
        Quantity = quantity;
        Price = price;
    }

    public override string ToString()
        => $"Produkt: {Product}, Menge: {Quantity}, Gesamt: {TotalCost}";
}

Beachte, dass wir die Properties explizit gemacht haben, sie mit init-Setter (immutable Objekte) versehen und die automatische Berechnung der Kosten hinzugefügt haben – der Code ist jetzt viel flexibler!

Aufruf im Programm:

var order = new Order("Fahrrad", 2, 15000);
Console.WriteLine(order); // Produkt: Fahrrad, Menge: 2, Gesamt: 30000

3. record struct

Evolution der structs

Vor record struct waren structs in C# die „einfachen Arbeitspferde“ – schnell kopiert, auf dem Stack gespeichert, super für kleine Datenpakete (z.B. Koordinaten oder Farben). Aber sie hatten nicht alle coolen Features von records: kein positionsbasierter Syntax, with-Expressions, kein Value-Vergleich by default und andere „Leckereien“.

Jetzt kannst du in C# structs im record-Style deklarieren:

public record struct Point(int X, int Y);
Positionsbasierter Syntax für record struct

Was bringt das überhaupt?

  • Automatische Implementierung von Equals, GetHashCode, ToString – deine Struktur kann jetzt easy verglichen und hübsch ausgegeben werden!
  • with-Cloning-Syntax: var p2 = p1 with { X = 10 };
  • Du kannst positionsbasierten oder expliziten Syntax verwenden.

Vergleich: klassischer struct VS record struct

struct record struct
Positionsbasierter Syntax Nein Ja
with-Expression Nein Ja
Immutability Nein (default) Ja (init)
Vergleich nach Wert Nein (default) Ja
ToString Standard Schick

Expliziter Body-Syntax für record struct

Genau wie beim normalen record, nur eben struct:


public record struct Rectangle
{
    public int Width { get; init; }
    public int Height { get; init; }

    public int Area => Width * Height;

    public Rectangle(int width, int height)
    {
        Width = width > 0 ? width : throw new ArgumentException("Breite > 0");
        Height = height > 0 ? height : throw new ArgumentException("Höhe > 0");
    }

    public void Print()
    {
        Console.WriteLine($"Größe: {Width} x {Height}, Fläche: {Area}");
    }
}

Beispiel für die Nutzung:

var rect = new Rectangle(10, 7);
rect.Print(); // Größe: 10 x 7, Fläche: 70

// Klonen und Breite ändern, Original bleibt gleich
var wideRect = rect with { Width = 20 };
wideRect.Print(); // Größe: 20 x 7, Fläche: 140

Besonderheiten von record struct

  • Es ist immer noch ein struct – value type. Wird beim Zuweisen kopiert!
  • Alle Vorteile von records: Vergleich nach Wert, with-Cloning, schickes ToString.
  • Empfohlen für kleine, kompakte, immutable Datensätze, wenn du Heap-Allocations vermeiden willst.
  • Du kannst positionsbasierte Parameter oder einen expliziten Body verwenden.

4. Typische Fehler und Stolperfallen

Zu viel Mutability: record struct macht Felder nicht automatisch immutable, wenn du sie als normale Felder deklarierst (z.B. public int Value;). Nutze init-Setter für wirklich immutable structs!

Vergleich: Wenn du neue Felder manuell hinzufügst (ohne sie im positionsbasierten Syntax zu haben), gilt: Nur die Felder im Konstruktor oder mit init-Setter werden beim automatischen Value-Vergleich berücksichtigt.

Kopieren: Es ist ein struct, also... alles wird kopiert! Nicht mit referenzbasierten records verwechseln.

Verwirrung mit with-Expressions: Sie machen immer nur ein shallow copy, also KEIN deep copy von verschachtelten Objekten.

Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION