Sök…


Statiskt initialiserad Singleton

public class Singleton
{
    private readonly static Singleton instance = new Singleton();
    private Singleton() { }
    public static Singleton Instance => instance;
}

Denna implementering är trådsäker eftersom i detta fall instance objektet initieras i den statiska konstruktör. CLR säkerställer redan att alla statiska konstruktörer utförs gängsäkra.

Muterande instance är inte en trådsäker operation, därför garanterar det readonly attributet immutability efter initiering.

Lazy, gängsäker Singleton (med hjälp av Double Checked Locking)

Den här trådsäkra versionen av en singleton var nödvändig i de tidiga versionerna av .NET där static initiering inte garanterades att vara trådsäker. I mer moderna versioner av ramverket föredras vanligtvis en statiskt initialiserad singleton eftersom det är mycket lätt att göra implementeringsfel i följande mönster.

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

Observera att kontrollen if (instance == null) görs två gånger: en gång innan låset förvärvas och en gång därefter. Denna implementering skulle fortfarande vara trådsäker även utan den första nollkontrollen. Det skulle dock innebära att ett lås skulle förvärvas varje gång instansen begärs, och det skulle leda till att prestandan lider. Den första nollkontrollen läggs till så att låset inte förvärvas om det inte är nödvändigt. Den andra nollkontrollen ser till att endast den första tråden som skaffar låset skapar förekomsten. De andra trådarna hittar instansen som ska fyllas i och hoppar framåt.

Lazy, tråd-säker Singleton (med Lazy )

.Net 4.0 typ Lazy garanterar tråd-säker objektinitialisering, så denna typ kan användas för att göra 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() { }
}

Att använda Lazy<T> kommer att se till att objektet bara instanseras när det används någonstans i samtalskoden.

En enkel användning kommer att vara:

using System;
                    
public class Program
{
    public static void Main()
    {
        var instance = LazySingleton.Instance;
    }
}

Live-demonstration på .NET Fiddle

Lat, trådsäker singleton (för .NET 3.5 eller äldre, alternativ implementering)

Eftersom du i .NET 3.5 och äldre inte har Lazy<T> -klass använder du följande mönster:

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

Detta är inspirerat av Jon Skeet's blogginlägg .

Eftersom Nested klassen är kapslad och privat kommer inte inställningen av singleton-instansen att utlöses genom åtkomst till andra medlemmar i Sigleton klassen (till exempel en offentlig läsbar egendom, till exempel).

Kassera Singleton-instansen när den inte längre behövs

De flesta exempel visar instans och hålla ett LazySingleton objekt tills den ägda applikationen har avslutats, även om det objektet inte längre behövs av applikationen. En lösning på detta är att implementera IDisposable och ställa in objektinstansen till noll enligt följande:

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

Ovanstående kod kasserar instansen före ansökan avslutas men endast om konsumenterna ringer Dispose() på objektet efter varje användning. Eftersom det inte finns någon garanti för att detta kommer att hända eller ett sätt att tvinga det, finns det heller ingen garanti för att instansen någonsin kommer att bortskaffas. Men om den här klassen används internt är det lättare att se till att Dispose() metoden kallas efter varje användning. Ett exempel följer:

public class Program
{
    public static void Main()
    {
        using (var instance = LazySingleton.Instance)
        {
            // Do work with instance
        }
    }
}

Observera att detta exempel inte är trådsäkert .



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow