0Pricing
C# Academy · Lektion

Unveränderlichkeit mit init und with

Verwenden Sie Setter, die nur mit init gesetzt werden können, um unveränderliche Objekte zu erstellen, und with-Ausdrücke für nicht-destruktiv veränderte Kopien.

Unveränderlichkeit mit init und with ist eine kostenlose C# Academy-Lektion auf CoddyKit. Dies ist Lektion 2 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des C# Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der C# Academy-Kurs umfasst insgesamt 4 Lektionen.

Was ist Unveränderlichkeit

Ein unveränderliches Objekt kann nach seiner Erstellung nicht mehr geändert werden. Unveränderlichkeit verhindert versehentliche Änderungen, macht Objekte standardmäßig threadsicher und erleichtert das Verständnis von Code. C# stellt init-Setter und with-Ausdrücke bereit, um Unveränderlichkeit komfortabel umzusetzen.

Nur-init-Setter

Ein init-Accessor ermöglicht das Setzen einer Eigenschaft während der Objektinitialisierung (im Konstruktor oder Objektinitialisierer), aber nicht danach. Er ist das unveränderliche Gegenstück zu set.

public class Point
{
    public double X { get; init; }
    public double Y { get; init; }
}

var p = new Point { X = 3.0, Y = 4.0 }; // OK — init phase
p.X = 5.0; // COMPILE ERROR — cannot set after init

// All of these are valid init-phase assignments:
var p2 = new Point(X: 1.0, Y: 2.0);
var p3 = new Point { X = 0, Y = 0 };

init in positionalen Record-Eigenschaften

Positionale Record-Eigenschaften sind standardmäßig nur mit init setzbar – das macht Records unveränderlich. Der Compiler generiert für jeden positionalen Parameter get; init;.

// This record:
public record Person(string Name, int Age);

// Is equivalent to:
public record Person
{
    public string Name { get; init; }
    public int Age    { get; init; }
    public Person(string Name, int Age) { this.Name = Name; this.Age = Age; }
    // + Deconstruct, Equals, GetHashCode, ToString
}

var alice = new Person("Alice", 30);
alice.Name = "Bob"; // COMPILE ERROR

with-Ausdrücke: nicht-destruktive Änderung

Ein with-Ausdruck erstellt eine Kopie eines Records, bei der bestimmte Eigenschaften geändert sind. Das Original bleibt unverändert – echte Unveränderlichkeit.

public record Person(string Name, int Age, string Email);

var alice = new Person("Alice", 30, "alice@example.com");

// Create a modified copy — alice is unchanged
var olderAlice = alice with { Age = 31 };
var renamed    = alice with { Name = "Alicia", Email = "alicia@example.com" };

Console.WriteLine(alice.Name);      // Alice (unchanged)
Console.WriteLine(olderAlice.Age);  // 31
Console.WriteLine(renamed.Name);    // Alicia

with bei Nicht-Record-Typen

C# 10 erlaubt with für jeden struct oder jede Klasse mit der Semantik eines Kopierkonstruktors – am natürlichsten ist die Verwendung jedoch mit Records. Bei Klassen müssen Sie dies manuell implementieren.

// struct with with-expression:
public struct Temperature
{
    public double Celsius { get; init; }
    public double Fahrenheit => Celsius * 9 / 5 + 32;
}

var t1 = new Temperature { Celsius = 20 };
var t2 = t1 with { Celsius = 25 }; // copy with change
Console.WriteLine(t1.Celsius); // 20 — unchanged
Console.WriteLine(t2.Celsius); // 25

Verkettung von with-Ausdrücken

Verketten Sie mehrere with-Ausdrücke, um komplexe Transformationen schrittweise aufzubauen, wobei jeder Schritt einen neuen unveränderlichen Wert aus dem vorherigen erstellt.

public record Order(int Id, string Status, decimal Total, DateTime UpdatedAt);

var order = new Order(42, "Pending", 99.99m, DateTime.UtcNow);

// Apply a promotion discount then mark as confirmed
var finalOrder = order
    with { Total    = order.Total * 0.9m }    // 10% off
    with { Status   = "Confirmed" }
    with { UpdatedAt = DateTime.UtcNow };

Console.WriteLine(order.Status);     // Pending (original unchanged)
Console.WriteLine(finalOrder.Status); // Confirmed

Berechnete Eigenschaften in unveränderlichen Typen

Abgeleitete Eigenschaften unveränderlicher Typen sind von Natur aus rein – sie werden aus den unveränderlichen Eigenschaftswerten berechnet und liefern für dieselbe Eingabe stets dasselbe Ergebnis.

public record Money(decimal Amount, string Currency)
{
    public Money Add(Money other)
    {
        if (Currency != other.Currency)
            throw new InvalidOperationException("Currency mismatch");
        return this with { Amount = Amount + other.Amount };
    }

    public Money Multiply(decimal factor) =>
        this with { Amount = Amount * factor };

    public override string ToString() =>
        $"{Amount:F2} {Currency}";
}

var price = new Money(10.00m, "USD");
var tax   = price.Multiply(0.08m);
var total = price.Add(tax);
Console.WriteLine(total); // 10.80 USD

Unveränderliche Sammlungen

Kombinieren Sie Records mit ImmutableList<T> und anderen Typen aus System.Collections.Immutable, um vollständig unveränderliche Datenstrukturen zu erhalten.

using System.Collections.Immutable;

public record ShoppingCart(
    string UserId,
    ImmutableList<CartItem> Items)
{
    public ShoppingCart AddItem(CartItem item) =>
        this with { Items = Items.Add(item) };

    public ShoppingCart RemoveItem(int productId) =>
        this with { Items = Items.RemoveAll(i => i.ProductId == productId) };

    public decimal Total => Items.Sum(i => i.Price * i.Quantity);
}

Threadsicherheit durch Unveränderlichkeit

Unveränderliche Objekte sind von sich aus threadsicher – beim Teilen über mehrere Threads ist keine Synchronisierung erforderlich, da sich ihr Zustand nach der Erstellung nicht ändern kann.

// Immutable config record shared across all threads safely
public record AppConfig(
    string ConnectionString,
    int MaxRetries,
    TimeSpan Timeout);

// Register as singleton — safe because record is immutable
builder.Services.AddSingleton(
    new AppConfig(
        ConnectionString: config["DB"]!,
        MaxRetries: 3,
        Timeout: TimeSpan.FromSeconds(30)));

// Any thread can read this simultaneously without locks

Praxisbeispiel: funktionales Event Sourcing

Unveränderliche Records eignen sich hervorragend für Event Sourcing: Jedes Domänenereignis ist unveränderlich, und der Zustand wird durch Falten der Ereignisse abgeleitet – keine Änderungen, keine Überraschungen.

public record OrderState(
    int Id,
    string Status = "Draft",
    decimal Total = 0m);

public static OrderState Apply(OrderState state, object evt) => evt switch
{
    OrderPlaced   e => state with { Status = "Pending", Total = e.Total },
    OrderShipped  _ => state with { Status = "Shipped" },
    OrderCancelled _ => state with { Status = "Cancelled" },
    _ => state
};

// Fold events to get current state:
var state = events.Aggregate(
    new OrderState(id),
    Apply);

Schnelltest

Was bewirkt ein „with“-Ausdruck für den ursprünglichen Record?

Zusammenfassung: Unveränderlichkeit mit init und with

Wichtige Erkenntnisse:

  • init-Accessor: nur während der Initialisierung setzbar, danach nicht mehr
  • Positionale Record-Eigenschaften sind standardmäßig nur mit init setzbar
  • with-Ausdruck: erstellt eine geänderte Kopie – das Original bleibt unverändert
  • Verketten Sie with-Ausdrücke für mehrstufige Transformationen
  • Unveränderliche Typen sind ohne Synchronisierung threadsicher
  • Kombinieren Sie sie mit ImmutableList<T> für vollständig unveränderliche Objektstrukturen

Häufig gestellte Fragen

Ist die Lektion „Unveränderlichkeit mit init und with“ kostenlos?

Ja — der vollständige Text von „Unveränderlichkeit mit init und with“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des C# Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der C# Academy-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Unveränderlichkeit mit init und with“?

Verwenden Sie Setter, die nur mit init gesetzt werden können, um unveränderliche Objekte zu erstellen, und with-Ausdrücke für nicht-destruktiv veränderte Kopien. Du übst C# Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um C# Academy zu starten?

Keine Vorkenntnisse erforderlich. C# Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 2 von 4.

Wie lange dauert die Lektion „Unveränderlichkeit mit init und with“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser C# Academy-Lektion Code schreiben und ausführen?

Ja. Jede C# Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. Record-Typen: Grundlagen und Syntax
  2. Unveränderlichkeit mit init und with
  3. Wertgleichheit und Dekonstruktion
  4. Records im Domain-Driven Design
← Zurück zu C# Academy