0Pricing
C# Academy · Lezione

Immutabilità con init e with

Usi setter accessibili solo con init per creare oggetti immutabili ed espressioni with per produrre copie modificate in modo non distruttivo.

Immutabilità con init e with è una lezione C# Academy gratuita su CoddyKit. Questa è la lezione 2 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento C# Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso C# Academy include 4 lezioni in totale.

Che cos'è l'immutabilità?

Un oggetto immutabile non può essere modificato dopo la creazione. L'immutabilità impedisce modifiche accidentali, rende gli oggetti thread-safe per impostazione predefinita e semplifica la comprensione del codice. C# fornisce gli accessor init e le espressioni with per rendere l'immutabilità più pratica.

Accessor init-only

Un accessor init consente di impostare una proprietà durante l'inizializzazione dell'oggetto (nel costruttore o in un inizializzatore di oggetto), ma mai in seguito. È l'equivalente immutabile di 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 nelle proprietà posizionali dei record

Le proprietà posizionali dei record sono init-only per impostazione predefinita: è questo che rende immutabili i record. Il compilatore genera get; init; per ogni parametro posizionale.

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

Espressioni with: modifica non distruttiva

Un'espressione with crea una copia di un record modificando proprietà specifiche. L'originale non cambia: è vera immutabilità.

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 sui tipi non record

C# 10 consente di utilizzare with su qualsiasi struct o classe che abbia la semantica di un costruttore di copia, ma il suo utilizzo è più naturale con i record. Per le classi è necessario implementarlo manualmente.

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

Concatenazione delle espressioni with

È possibile concatenare più espressioni with per creare trasformazioni complesse, generando a ogni passaggio un nuovo valore immutabile a partire da quello precedente.

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

Proprietà calcolate nei tipi immutabili

Le proprietà derivate dei tipi immutabili sono naturalmente pure: eseguono il calcolo a partire dai valori fissi delle proprietà e restituiscono sempre lo stesso risultato per lo stesso input.

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

Raccolte immutabili

Combinare i record con ImmutableList<T> e con altri tipi di System.Collections.Immutable per ottenere strutture dati completamente immutabili.

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

Thread safety grazie all'immutabilità

Gli oggetti immutabili sono intrinsecamente thread-safe: non è necessaria alcuna sincronizzazione quando vengono condivisi tra thread, perché il loro stato non può cambiare dopo la costruzione.

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

Caso reale: event sourcing funzionale

I record immutabili si combinano naturalmente con l'event sourcing: ogni evento di dominio è immutabile e lo stato viene derivato applicando un fold agli eventi, senza modifiche e senza sorprese.

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

Verifica rapida

Che cosa fa un'espressione 'with' al record originale?

Riepilogo: immutabilità con init e with

Punti chiave:

  • Accessor init: impostabile solo durante l'inizializzazione, mai in seguito
  • Le proprietà posizionali dei record sono init-only per impostazione predefinita
  • Espressione with: crea una copia modificata; l'originale non cambia
  • Concatenare le espressioni with per trasformazioni in più passaggi
  • I tipi immutabili sono thread-safe senza sincronizzazione
  • Combinare con ImmutableList<T> per ottenere grafi di oggetti completamente immutabili

Domande Frequenti

La lezione «Immutabilità con init e with» è gratuita?

Sì — il testo completo di «Immutabilità con init e with» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso C# Academy, passa a CoddyKit PRO. Il corso C# Academy include 4 lezioni in totale.

Cosa imparerò in «Immutabilità con init e with»?

Usi setter accessibili solo con init per creare oggetti immutabili ed espressioni with per produrre copie modificate in modo non distruttivo. Eserciti C# Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare C# Academy?

Non è richiesta alcuna esperienza precedente. C# Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 4.

Quanto tempo richiede la lezione «Immutabilità con init e with»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione C# Academy?

Sì. Ogni lezione C# Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Tipi record: basi e sintassi
  2. Immutabilità con init e with
  3. Uguaglianza per valore e destrutturazione
  4. Record nel Domain-Driven Design
← Torna a C# Academy