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

Live Demo su .NET Fiddle

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 .



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow