C# Academy · Lektion

Uforanderlighed med init og with

Brug init-only-settere til at oprette uforanderlige objekter og with-udtryk til at fremstille kopier med ikke-destruktive ændringer.

Lektion 2 af 412 trin

Uforanderlighed med init og with er en gratis C# Academy-lektion på CoddyKit. Dette er lektion 2 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i C# Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. C# Academy-kurset indeholder 4 lektioner i alt.

Hvad er uforanderlighed?

Et uforanderligt objekt kan ikke ændres efter oprettelsen. Uforanderlighed forhindrer utilsigtede ændringer, gør objekter trådsikre som standard og gør det nemmere at forstå koden. C# har init-settere og with-udtryk, som gør det enkelt at arbejde uforanderligt.

init-only-settere

En init-accessor gør det muligt at indstille en egenskab under objektinitialisering (i konstruktøren eller en objektinitialisering), men aldrig derefter. Det er den uforanderlige pendant til 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 i positionsbaserede egenskaber i recordtyper

Positionsbaserede egenskaber i recordtyper er som standard kun init — det er det, der gør recordtyper uforanderlige. Compileren genererer get; init; for hver positionsparameter.

// 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-udtryk: ikke-destruktiv ændring

Et with-udtryk opretter en kopi af en recordtype, hvor bestemte egenskaber er ændret. Den oprindelige recordtype forbliver uændret — ægte uforanderlighed.

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 på andre typer end recordtyper

C# 10 tillader with på enhver struktur eller klasse med semantik for kopikonstruktører — men det er mest naturligt med recordtyper. For klasser skal du implementere det manuelt.

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

Kædning af with-udtryk

Kæd flere with-udtryk for at opbygge komplekse transformationer, hvor hvert trin opretter en ny uforanderlig værdi ud fra den foregående.

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

Beregnede egenskaber på uforanderlige typer

Afledte egenskaber på uforanderlige typer er naturligt rene — de beregnes ud fra de faste egenskabsværdier og returnerer altid det samme resultat for de samme inddata.

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

Uforanderlige samlinger

Kombinér recordtyper med ImmutableList<T> og andre typer fra System.Collections.Immutable for at få fuldt uforanderlige datastrukturer.

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

Trådsikkerhed gennem uforanderlighed

Uforanderlige objekter er i sig selv trådsikre — der kræves ingen synkronisering, når de deles mellem tråde, fordi deres tilstand ikke kan ændres efter konstruktionen.

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

Virkeligt eksempel: Funktionel event sourcing

Uforanderlige recordtyper passer naturligt sammen med event sourcing: hver domænehændelse er uforanderlig, og tilstanden udledes ved at folde hændelser — ingen ændringer, ingen overraskelser.

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);

Hurtigt tjek

Hvad gør et 'with'-udtryk ved den oprindelige recordtype?

Opsummering: Uforanderlighed med init og with

Vigtige pointer:

  • init-accessor: kan kun indstilles under initialisering, aldrig derefter
  • Positionsbaserede egenskaber i recordtyper er som standard kun init
  • with-udtryk: opretter en ændret kopi — den oprindelige værdi forbliver uændret
  • Kæd with-udtryk for transformationer i flere trin
  • Uforanderlige typer er trådsikre uden synkronisering
  • Kombinér med ImmutableList<T> for fuldt uforanderlige objektgrafer
Gratis at komme i gang

Lær C# med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
93
Lektioner
346

Ofte stillede spørgsmål

Er lektionen “Uforanderlighed med init og with” gratis?

Ja — hele teksten til “Uforanderlighed med init og with” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af C# Academy-kurset, skal du opgradere til CoddyKit PRO. C# Academy-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Uforanderlighed med init og with”?

Brug init-only-settere til at oprette uforanderlige objekter og with-udtryk til at fremstille kopier med ikke-destruktive ændringer. Du øver dig i C# Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på C# Academy?

Der kræves ingen tidligere erfaring. C# Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 2 af 4.

Hvor lang tid tager lektionen “Uforanderlighed med init og with”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne C# Academy-lektion?

Ja. Alle C# Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Record-typer: Grundlag og syntaks
  2. Uforanderlighed med init og with
  3. Værdilighed og deconstruction
  4. Records i Domain-Driven Design
← Tilbage til C# Academy