CodeGym /Corsi /C# SELF /required proprietà e p...

required proprietà e proprietà field

C# SELF
Livello 17 , Lezione 3
Disponibile

1. Introduzione

Programmare in C# senza proprietà si può, ma è un po' come andare sui rollerblade dentro la Casa Bianca — si può, ma è scomodo. Ormai ci siamo abituati alla sintassi corta senza getter e setter inutili, ma quando la nostra app inizia a lavorare con modelli "seri" (tipo una classe User che descrive un utente nel sistema), diventa importante garantire che tutti i dati necessari ci siano davvero.

Per esempio, se abbiamo una classe così:

public class User
{
    public string Nome { get; set; }
    public int Eta { get; set; }
}

È facile dimenticarsi di inizializzare le proprietà:

User user = new User(); // Nome sarà null, Eta = 0

Risultato: da qualche parte dopo dieci schermate e cento giri di logica ti becchi il famoso NullReferenceException e ci metti una vita a capire di chi è la colpa.

Ricordiamoci delle proprietà init-only

Sì, possiamo usare l'inizializzazione solo alla creazione:

public string Nome { get; init; }

Ma anche con questa sintassi nessuno obbliga l'utente della classe a passare un valore se non vuole — la maggior parte dei costruttori di default inizializzano i campi con i valori di default (null, 0 ecc.).

Quindi come facciamo a obbligare il programmatore, o anche noi stessi, a non dimenticare di mettere il valore giusto? Proprio per questo è stato inventato il modificatore required, apparso in C# 11.

2. Proprietà required: inizializzazione rigorosa

Il modificatore required dice al compilatore di controllare che una certa proprietà dell'oggetto sia impostata esplicitamente al momento della creazione. In pratica, se una proprietà così non è inizializzata quando crei l'oggetto — il compilatore non ti lascia passare e l'IDE ti disegna una bella onda rossa.


public class User
{
    public required string Nome { get; set; }
    public int Eta { get; set; }
}
Proprietà obbligatoria con required

Proviamo a creare un utente:

// Errore di compilazione: la proprietà Nome è obbligatoria da inizializzare!
User user1 = new User();

Oppure così:


// Errore di compilazione: non è stata indicata la proprietà required Nome
User user2 = new User { Eta = 18 };

Ecco invece il modo giusto:

User user3 = new User { Nome = "Germiona", Eta = 18 };

Come funziona required?

Il modificatore required dice al compilatore: "Dopo che il costruttore dell'oggetto (qualsiasi!) è finito, questa proprietà deve essere impostata esplicitamente".

Funziona sia per le proprietà normali che per quelle init-only:

public required string Nome { get; init; }

Se definisci un costruttore personalizzato che inizializza il valore della proprietà required, allora va bene lo stesso.

Schema tipico di controllo

Scenario Compilatore contento?
Proprietà required non impostata
Impostata nell'object initializer ✔️
Impostata nel costruttore ✔️

Esempio nel nostro modello "Cane"

public class Cane
{
    public required string Nome { get; set; }
    public int Eta { get; set; }
}

Cane cane = new Cane { Nome = "Bobik", Eta = 5 }; // Tutto ok!
Cane caneBrutto = new Cane { Eta = 2 }; // Errore! Nome non specificato

Dove aiuta davvero required?

  • Passaggio di DTO tra layer: Se hai un'API, serve che arrivino sempre tutti i campi necessari.
  • Modelli complessi con attributi obbligatori: Tipo Product con SKU obbligatorio, Order con numero obbligatorio.
  • Ai colloqui e code review: Se mostri questa sintassi, probabilmente il tuo codice verrà letto con rispetto (e un po' di invidia).

3. Come funziona required con i costruttori?

A volte scrivi tu il costruttore. Cosa succede se non inizializzi la proprietà required nel costruttore o nell'object initializer? Il compilatore ti dà errore.


public class Articolo
{
    public required string Titolo { get; set; }
    public required string Autore { get; set; }

    public Articolo()
    {
        // Se non inizializzi Titolo e Autore — errore di compilazione!
        // Puoi fare così:
        Titolo = "Bez nazvaniya";
        Autore = "Neizvestnyj";
    }
}

Se il costruttore assegna i valori alle proprietà required — tutto ok. Se no, devi inizializzare questi campi tramite l'object initializer (new Articolo { ... }).

Specificità d'uso

  • required funziona solo con le proprietà, non con i campi.
  • required non è ereditato — se la proprietà base è required, ma nella derivata non metti required, il compilatore non si lamenta (ma è buona pratica ripetere required anche nella derivata).
  • required non si può usare con i campi automatici o altro che non sia una property.

4. Proprietà con sintassi a freccia

Con le nuove versioni di C#, gli sviluppatori puntano sempre di più a codice compatto e chiaro. Una delle novità più evidenti per le proprietà è la sintassi a freccia (o expression-bodied properties).

A volte vuoi definire una proprietà che restituisce solo un valore senza logica extra. Prima dovevi scrivere il getter completo con le graffe:

public int Eta
{
    get { return annoNascita > 0 ? DateTime.Now.Year - annoNascita : 0; }
}

Ora puoi scrivere molto più corto — usando la freccia (=>):


public int Eta => annoNascita > 0 ? DateTime.Now.Year - annoNascita : 0;

Questa sintassi si chiama proprietà con corpo-espressione (expression-bodied property). È perfetta per calcoli semplici e rende il codice più compatto.

Esempio di uso di get e set

public class Libro
{
    private string _titolo;

    public string Titolo
    {
        get => _titolo;
        set => _titolo = value.Trim();
    }
}

Qui get restituisce il valore del campo, e set — assegna, togliendo prima gli spazi ai lati.

Esempio di uso solo di get (sola lettura):

Se la proprietà è sola lettura — puoi scriverla senza graffe, solo con =>.

public class Persona
{
    private string nome = "Mark Tven";

    // Sola lettura: proprietà calcolata
    public string Nome => nome.ToUpper();
}

Qui la proprietà Nome si può solo leggere — restituisce sempre nome in maiuscolo.

5. Parola chiave field per le proprietà

Prima di C# 14, se volevi accedere al campo nascosto di una proprietà automatica direttamente nel setter o getter (tipo per evitare ricorsione o aggiungere logica tua) — non si poteva. Dovevi dichiarare il campo a mano.

C# 14 ti permette di accedere al campo nascosto della proprietà automatica con la parola chiave field:


public class Persona
{
    public string Nome
    {
        get => field; // field — è il campo nascosto della proprietà Nome
        set
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException("Il nome non può essere vuoto!");
            field = value; // usiamo field invece di _nome
        }
    }
}

Prima dovevi fare così:

private string _nome;
public string Nome
{
    get => _nome;
    set
    {
        if (string.IsNullOrWhiteSpace(value))
            throw new ArgumentException("Il nome non può essere vuoto!");
        _nome = value;
    }
}

Una dichiarazione di variabile in meno. Più il codice è compatto, meglio è.

Perché a volte è importante accedere proprio al campo dentro la proprietà?

  • A volte vuoi controllare esplicitamente dove e come viene salvato il valore (tipo se vuoi restituire una copia dell'oggetto, fare caching o lazy-loading).
  • Se vuoi usare attributi o reflection — il campo può servirti per nome.
  • In certi casi di (de)serializzazione o performance-tuning vuoi più controllo su come viene salvato il valore.

Varianti d'uso:

Validazione dati nel setter

public double Voto
{
    get => field;
    set
    {
        if (value < 0 || value > 5)
            throw new ArgumentOutOfRangeException("La valutazione deve essere da 0 a 5");
        field = value;
    }
}

Statistiche sui cambi di valore

public int ConteggioPassi
{
    get => field;
    set
    {
        if (value > field)
        {
            Console.WriteLine($"Evviva! Hai fatto {value - field} passi in più!");
        }
        field = value;
    }
}

Lazy Load

public string Dati
{
    get
    {
        if (field == null)
            field = CaricaDatiDalDatabase();
        return field;
    }
    set => field = value;
}

6. Errori tipici e sfumature

Errore n°1: hai dimenticato di inizializzare la proprietà required.
Il compilatore non ti lascia passare con questo codice e ti dà errore subito in compilazione, così eviti problemi a runtime.

Errore n°2: le proprietà required non funzionano senza inizializzazione completa nel costruttore.
Se il costruttore non prende i valori per tutte le proprietà obbligatorie, il compilatore ti ricorda che ti sei dimenticato qualcosa.

Errore n°3: tentativo di usare required con const o readonly.
Questi modificatori non sono compatibili — required si può usare solo con le proprietà normali. Se provi a combinarli, ottieni errore.

2
Compito
C# SELF, livello 17, lezione 3
Bloccato
Creazione di una classe con proprietà required
Creazione di una classe con proprietà required
2
Compito
C# SELF, livello 17, lezione 3
Bloccato
Logica personalizzata nei getter e setter usando field
Logica personalizzata nei getter e setter usando field
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION