static indica un membro che appartiene al tipo, non a un oggetto. Un campo di istanza esiste una volta per ogni oggetto; un campo statico esiste una volta sola, punto. Un metodo di istanza viene eseguito su un oggetto (order.Ship()); un metodo statico viene eseguito sul tipo (Math.Round(x)) e non ha bisogno di alcun oggetto.
Campi statici: una sola copia, condivisa
Il modo più chiaro per vedere la differenza è un contatore. Ogni Order ha il suo Id, ma il numero di ordini creati finora deve stare in un posto condiviso.
Output:
Ana #1, Ben #2, Chen #3
Orders created: 3
Ai membri statici si accede tramite il nome del tipo: Order.Created, non a.Created. C# non permette di raggiungere un membro statico attraverso un'istanza (errore CS0176), e questo rende evidente nel punto di chiamata che il valore è condiviso.
Metodi statici
Un metodo statico non ha this. Lavora solo con i suoi parametri e con altri membri statici. Questo lo rende la forma naturale per le funzioni di supporto che calcolano qualcosa a partire dai loro input:
Output:
61.38 EUR
7
Anche Main è statico: il runtime lo chiama prima che esista qualsiasi oggetto.
L'errore CS0120: codice statico che usa membri di istanza
Poiché Main è statico, chi inizia incontra spesso questo errore appena aggiunge un secondo metodo:
class Program
{
int visits;
void Greet() { Console.WriteLine("Hello"); }
static void Main()
{
Greet(); // error CS0120: An object reference is required for the non-static field, method, or property 'Program.Greet()'
visits++; // error CS0120: An object reference is required for the non-static field, method, or property 'Program.visits'
}
}
Un metodo statico non ha un oggetto, quindi non c'è alcun visits da incrementare. Due soluzioni, e quella giusta dipende da cosa è il membro:
- Il membro è davvero un dato del singolo oggetto: crea un oggetto e usalo.
var app = new Program(); app.Greet(); - Il membro non ha bisogno dello stato dell'oggetto: marcalo come
static.static void Greet() { ... }
Marcare tutto come static per far sparire l'errore funziona in un programma minuscolo e diventa stato globale in uno reale. Decidi chiedendoti se il dato appartiene a un oggetto o all'intero programma.
La direzione opposta è sempre consentita: i metodi di istanza possono chiamare liberamente i membri statici.
Classi statiche
Una classe dichiarata static può contenere solo membri statici e non può mai essere istanziata. È un gruppo con un nome di funzioni e costanti.
Output:
77
100
True
Il compilatore fa rispettare il contratto: un membro di istanza dentro una classe statica è l'errore CS0708, e usare new su di essa è l'errore CS0712. Math, Console, File, Path ed Enumerable sono tutte classi statiche. Anche gli extension method devono stare in una classe statica.
Costruttori statici e static readonly
Un campo statico il cui valore richiede più di un'espressione si può impostare in un costruttore statico, che il runtime esegue una sola volta, prima del primo utilizzo del tipo:
Output:
Before first use
ShippingRates initialized
5.50
3
2026-01-01
readonly significa che il campo si può assegnare solo nel suo inizializzatore o nel costruttore statico. Dopo non si può riassegnare, anche se l'oggetto a cui punta potrebbe restare modificabile, ed è per questo che il dizionario è esposto come IReadOnlyDictionary.
const o static readonly
Entrambi ti danno un valore con un nome condiviso da tutto il programma, e differiscono per il momento in cui il valore viene fissato:
const | static readonly | |
|---|---|---|
| Valore fissato | in fase di compilazione | in esecuzione, una volta |
| Tipi ammessi | numeri, char, bool, string, enum, null | qualsiasi tipo |
| Implicitamente statico | sì (non si può scrivere static const) | dichiarato static in modo esplicito |
| Copiato negli assembly chiamanti | sì | no, viene letto dal campo |
Usabile nei switch case e negli argomenti degli attributi | sì | no |
La riga sulla copia conta per le librerie: se l'assembly A dichiara public const int MaxItems = 50; e l'assembly B la usa, B contiene il letterale 50. Cambiare A in 100 non ha alcun effetto su B finché B non viene ricompilato. Per valori che potrebbero cambiare tra una versione e l'altra, preferisci static readonly.
using static
Una direttiva using static (C# 6) importa i membri statici di un tipo, così puoi chiamarli senza il nome del tipo:
Output:
Area: 28.27
40
Si legge bene nel codice pieno di calcoli matematici. Altrove usala con moderazione: un WriteLine o un Parse da soli nascondono il tipo da cui arriva il metodo.
Stato statico e thread
Un campo statico è condiviso da tutti i thread del processo. Incrementarlo da più thread contemporaneamente fa perdere aggiornamenti, perché created++ è una lettura, una somma e una scrittura, e due thread possono intrecciarle. Usa Interlocked.Increment(ref created) per i contatori, o un lock per qualsiasi cosa più grande. Le applicazioni web sono il posto dove questo morde di solito: ogni richiesta gira su un qualche thread, quindi un campo statico che contiene "l'utente corrente" è condiviso da tutti gli utenti.
Un'altra sottigliezza: in una classe generica, ogni tipo costruito ha i propri campi statici. Cache<int>.Count e Cache<string>.Count sono due campi diversi.
Quando usare static
Buoni usi:
- Funzioni di supporto pure che calcolano un risultato dai loro argomenti: formattazione, conversioni, validazione.
- Costanti e dati di consultazione in sola lettura condivisi dal programma.
- Metodi factory, come
TimeSpan.FromMinutes(5)o il tuoTemperature.FromCelsius(21). - Extension method.
Usi sbagliati:
- Stato globale modificabile (l'utente corrente, un carrello, impostazioni che cambiano). Lega tra loro tutte le parti di codice che lo toccano e fa dipendere i test dagli avanzi degli altri test.
- Tutto ciò che vorrai sostituire nei test, come un orologio, un database o un servizio che invia email. Le chiamate statiche non si possono sostituire con un fake; un'istanza passata al costruttore sì.
Domande frequenti
Cosa significa static in C#?
Un membro static appartiene al tipo stesso invece che a un oggetto. Di un campo statico esiste una sola copia, qualunque sia il numero di oggetti, e un metodo statico si chiama sul nome del tipo (Math.Max(3, 7)) senza creare un'istanza. Un metodo statico non ha this, quindi non può usare direttamente i campi di istanza.
Cos'è una classe statica in C#?
Una classe dichiarata static può contenere solo membri statici e non può mai essere istanziata (usare new su di essa è l'errore CS0712). Raggruppa funzioni correlate che non hanno bisogno dello stato di un oggetto, come Math, Console o File. Gli extension method devono essere dichiarati in una classe statica.
Come risolvo "An object reference is required for the non-static field, method, or property"?
È l'errore CS0120: un metodo statico, spesso Main, sta usando un membro di istanza. Crea un oggetto e chiama il membro su di esso (var app = new Program(); app.Run();), oppure marca il membro come static se non ha bisogno di dati propri di ogni oggetto.
Che differenza c'è tra const e static readonly in C#?
Una const è una costante in fase di compilazione (numeri, stringhe, bool, enum) il cui valore viene copiato in ogni assembly che la usa. Un campo static readonly viene assegnato una sola volta in esecuzione, nel suo inizializzatore o nel costruttore statico, e può contenere qualsiasi tipo, come un DateTime o una lista. Usa const per i valori che non cambieranno mai, static readonly per tutto il resto.
Quando conviene usare static in C#?
Usa static per il codice che lavora solo sui suoi parametri (funzioni di supporto pure come un formattatore di prezzi), per costanti e cache davvero globali per il programma e per i metodi factory. Evita static per tutto ciò che contiene uno stato per utente o per richiesta, o che vorrai sostituire nei test: lo stato modificabile condiviso è difficile da testare e non è sicuro tra thread senza lock.