Singleton thread-safe
Fa parte della sezione Programmazione orientata agli oggetti del percorso C# di Coddy. Lezione 52 di 70.
Il pattern Singleton garantisce che una classe abbia una sola istanza nell’intera applicazione e fornisce un punto di accesso globale a essa. È utile per gestire risorse condivise come le impostazioni di configurazione, i servizi di registrazione o le connessioni al database.
Un Singleton di base usa un costruttore privato per impedire l'istanziazione esterna e una proprietà statica per fornire l'accesso:
public class Logger
{
private static Logger _instance;
private static readonly object _lock = new object();
private Logger() { } // Costruttore privato
public static Logger Instance
{
get
{
lock (_lock)
{
if (_instance == null)
_instance = new Logger();
return _instance;
}
}
}
public void Log(string message) => Console.WriteLine(message);
}L'istruzione lock garantisce la sicurezza dei thread: se più thread provano ad accedere a Instance contemporaneamente, solo uno alla volta può entrare nella sezione critica. Senza questa istruzione, due thread potrebbero vedere entrambi _instance come null e creare istanze separate.
C# offre un approccio più semplice e thread-safe tramite l'inizializzazione statica:
public class Logger
{
private static readonly Logger _instance = new Logger();
private Logger() { }
public static Logger Instance => _instance;
public void Log(string message) => Console.WriteLine(message);
}Questa versione è thread-safe perché il CLR garantisce che l’inizializzazione dei campi statici avvenga una sola volta. Entrambi gli approcci garantiscono che tu ottenga sempre la stessa istanza:
Logger.Instance.Log("First call");
Logger.Instance.Log("Same instance");Sfida
FacileCreiamo un gestore della configurazione thread-safe usando il pattern Singleton. Nelle applicazioni reali, le impostazioni di configurazione devono essere accessibili in tutto il programma, assicurando al contempo che esista una sola istanza: un caso d’uso perfetto per Singleton.
Organizzerai il codice in due file:
ConfigManager.cs: crea una classeConfigManagernello spazio dei nomiConfigurationche implementa il pattern Singleton. Il gestore della configurazione deve:- Avere un campo statico privato che contenga l’unica istanza
- Usare un costruttore privato per impedire la creazione di istanze dall’esterno
- Fornire una proprietà statica
Instanceche restituisca l’unica istanza (usa l’approccio più semplice di inizializzazione statica per garantire la sicurezza dei thread) - Includere un campo stringa privato
_appNameinizializzato a"MyApp" - Avere un metodo
SetAppName(string name)per aggiornare il nome dell’app - Avere un metodo
GetAppName()che restituisca il nome attuale dell’app
Program.cs: nel file principale, dimostra che Singleton funziona correttamente accedendo più volte all’istanza e mostrando che le modifiche persistono in tutti i riferimenti.
Riceverai un input:
- Un nuovo nome dell’applicazione da impostare (ad es.
ProductionServer)
Nel tuo programma:
- Ottieni l’istanza di
ConfigManagere stampa il nome predefinito dell’app - Ottieni di nuovo l’istanza (assegnandola a una variabile diversa) e usala per impostare il nuovo nome dell’app ricevuto in input
- Ottieni l’istanza una terza volta e stampa il nome dell’app per dimostrare che tutti i riferimenti condividono gli stessi dati
Ad esempio, se l’input è ProductionServer, l’output dovrebbe essere:
MyApp
ProductionServerQuesto dimostra il vantaggio fondamentale di Singleton: non importa quante volte accedi a Instance, ottieni sempre lo stesso oggetto con uno stato condiviso. L’approccio di inizializzazione statica garantisce la sicurezza dei thread perché il CLR assicura che il campo statico venga inizializzato una sola volta, anche se più thread vi accedono simultaneamente.
Provalo tu
using System;
using Configuration;
class Program
{
public static void Main(string[] args)
{
// Leggi l'input - il nuovo nome dell'applicazione
string newAppName = Console.ReadLine();
// TODO: Ottieni l'istanza di ConfigManager e stampa il nome predefinito dell'applicazione
// TODO: Ottieni di nuovo l'istanza (in una variabile diversa) e usala per impostare il nuovo nome dell'applicazione
// TODO: Ottieni l'istanza una terza volta e stampa il nome dell'applicazione per dimostrare che tutti i riferimenti condividono gli stessi dati
}
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti della programmazione orientata agli oggetti
File esterniNamespace e direttiveIntroduzione a classi e oggettiLa parola chiave "this"Metodi e parametriCampi e proprietàCostruttoriInizializzatori di oggettiRiepilogo - Calcolatrice semplice4Ereditarietà
Sintassi di base dell'ereditarietà (:)La parola chiave 'base'Parole chiave virtual e overrideClassi sealedLa classe base 'object'Riepilogo - Gerarchia dei dipendenti7Funzionalità avanzate
Sovraccarico degli operatoriIndicizzatori (this[])Override di ToString()Metodi di estensioneRiepilogo - Lista personalizzata10Pattern di progettazione Parte 1
Introduzione ai pattern di progettazioneSingleton thread-safePattern FactoryPattern Observer (eventi)Pattern Strategy2Proprietà e membri statici
Proprietà implementate automaticamenteProprietà di sola lettura e di sola scritturaCampi e metodi staticiClassi staticheMembri con corpo di espressione5Polimorfismo e interfacce
Polimorfismo a compile time e runtimeInterfaccia o classe astrattaInterfacce multipleInterfacce espliciteUpcasting e downcastingRipasso: calcolatore di forme8Concetti avanzati di programmazione a oggetti
Composizione invece dell'ereditarietàTipi generici (classi e metodi)Delegati ed eventiAttributi e reflectionIDisposable e istruzione usingNozioni di base sull'iniezione delle dipendenze11Pattern di progettazione, parte 2
Pattern del ComandoPattern dell’AdattatorePattern del DecoratorePattern Template MethodPattern dello StatoPattern Composito3Architettura delle classi
Dati di istanza vs staticiParole chiave 'readonly' e 'const'Campi di supportoRiepilogo - Gestore di conti bancari6Incapsulamento
Modificatori di accessoProprietà per l'incapsulamentoImplementazione dell'occultamento dei datiModelli di immutabilitàRiepilogo - Registri degli studentiEsercitati da solo: Compilatore C# online