C# Language
Implementazione di Singleton
Ricerca…
Singleton inizializzato staticamente
public class Singleton
{
private readonly static Singleton instance = new Singleton();
private Singleton() { }
public static Singleton Instance => instance;
}
Questa implementazione è thread-safe perché in questo caso l'oggetto instance
viene inizializzato nel costruttore statico. Il CLR garantisce già che tutti i costruttori statici siano eseguiti thread-safe.
L' instance
mutante non è un'operazione thread-safe, pertanto l'attributo readonly
garantisce l'immutabilità dopo l'inizializzazione.
Singleton pigro e sicuro per i thread (utilizzando Double Checked Locking)
Questa versione thread-safe di un singleton era necessaria nelle prime versioni di .NET in cui l'inizializzazione static
non era garantita per essere thread-safe. Nelle versioni più moderne del framework di solito viene preferito un singleton inizializzato staticamente perché è molto facile commettere errori di implementazione nel seguente schema.
public sealed class ThreadSafeSingleton
{
private static volatile ThreadSafeSingleton instance;
private static object lockObject = new Object();
private ThreadSafeSingleton()
{
}
public static ThreadSafeSingleton Instance
{
get
{
if (instance == null)
{
lock (lockObject)
{
if (instance == null)
{
instance = new ThreadSafeSingleton();
}
}
}
return instance;
}
}
}
Si noti che il controllo if (instance == null)
viene eseguito due volte: una volta prima dell'acquisizione del blocco e una volta in seguito. Questa implementazione sarebbe ancora thread-safe anche senza il primo controllo nullo. Tuttavia, ciò significherebbe che un blocco sarebbe acquisito ogni volta che viene richiesta l'istanza, e ciò causerebbe sofferenza alle prestazioni. Il primo controllo nullo viene aggiunto in modo tale che il blocco non venga acquisito a meno che non sia necessario. Il secondo controllo nullo si assicura che solo il primo thread per acquisire il blocco crei l'istanza. Gli altri thread troveranno l'istanza da compilare e saltare in avanti.
Singleton pigro, sicuro per i thread (usando Pigro )
.Net 4.0 type Lazy garantisce l'inizializzazione degli oggetti thread-safe, quindi questo tipo potrebbe essere usato per creare Singletons.
public class LazySingleton
{
private static readonly Lazy<LazySingleton> _instance =
new Lazy<LazySingleton>(() => new LazySingleton());
public static LazySingleton Instance
{
get { return _instance.Value; }
}
private LazySingleton() { }
}
Usando Lazy<T>
ci si assicurerà che l'oggetto sia istanziato solo quando è usato da qualche parte nel codice chiamante.
Un semplice utilizzo sarà come:
using System;
public class Program
{
public static void Main()
{
var instance = LazySingleton.Instance;
}
}
Singleton Lazy, thread safe (per .NET 3.5 o versioni precedenti, implementazione alternativa)
Perché in .NET 3.5 e versioni precedenti non hai la classe Lazy<T>
tu usi il seguente modello:
public class Singleton
{
private Singleton() // prevents public instantiation
{
}
public static Singleton Instance
{
get
{
return Nested.instance;
}
}
private class Nested
{
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Nested()
{
}
internal static readonly Singleton instance = new Singleton();
}
}
Questo è ispirato al post sul blog di Jon Skeet .
Poiché la classe Nested
è nidificata e privata, l'istanza dell'istanza singleton non verrà attivata accedendo ad altri membri della classe Sigleton
(ad esempio, una proprietà pubblica di sola lettura, ad esempio).
Smaltimento dell'istanza Singleton quando non è più necessaria
La maggior parte degli esempi mostra la LazySingleton
istanze e il possesso di un oggetto LazySingleton
fino a quando l'applicazione proprietaria non è terminata, anche se tale oggetto non è più necessario dall'applicazione. Una soluzione è implementare IDisposable
e impostare l'istanza dell'oggetto su null come segue:
public class LazySingleton : IDisposable
{
private static volatile Lazy<LazySingleton> _instance;
private static volatile int _instanceCount = 0;
private bool _alreadyDisposed = false;
public static LazySingleton Instance
{
get
{
if (_instance == null)
_instance = new Lazy<LazySingleton>(() => new LazySingleton());
_instanceCount++;
return _instance.Value;
}
}
private LazySingleton() { }
// Public implementation of Dispose pattern callable by consumers.
public void Dispose()
{
if (--_instanceCount == 0) // No more references to this object.
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (_alreadyDisposed) return;
if (disposing)
{
_instance = null; // Allow GC to dispose of this instance.
// Free any other managed objects here.
}
// Free any unmanaged objects here.
_alreadyDisposed = true;
}
Il codice precedente elimina l'istanza prima della chiusura dell'applicazione, ma solo se i consumatori chiamano Dispose()
sull'oggetto dopo ogni utilizzo. Poiché non vi è alcuna garanzia che ciò accada o un modo per forzarlo, non vi è alcuna garanzia che l'istanza sarà mai smaltita. Ma se questa classe viene utilizzata internamente, è più facile assicurarsi che il metodo Dispose()
venga chiamato dopo ogni utilizzo. Segue un esempio:
public class Program
{
public static void Main()
{
using (var instance = LazySingleton.Instance)
{
// Do work with instance
}
}
}
Si prega di notare che questo esempio non è thread-safe .