C# Language
Singleton Implementatie
Zoeken…
Statisch geïnitialiseerde Singleton
public class Singleton
{
private readonly static Singleton instance = new Singleton();
private Singleton() { }
public static Singleton Instance => instance;
}
Deze implementatie is thread-safe omdat in dit geval instance
object wordt geïnitialiseerd in de statische aannemer. De CLR zorgt er al voor dat alle statische constructors thread-safe worden uitgevoerd.
Muterende instance
is geen thread-veilige bewerking, daarom garandeert het readonly
lezenattribuut onveranderlijkheid na initialisatie.
Luie, draadveilige Singleton (met Double Checked Locking)
Deze thread-veilige versie van een singleton was nodig in de vroege versies van .NET waar niet kon worden gegarandeerd dat static
initialisatie thread-safe was. In modernere versies van het framework wordt meestal de voorkeur gegeven aan een statisch geïnitialiseerde singleton omdat het heel eenvoudig is om implementatiefouten in het volgende patroon te maken.
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;
}
}
}
Merk op dat de if (instance == null)
-controle twee keer wordt uitgevoerd: een keer voordat de vergrendeling is verkregen en een keer daarna. Deze implementatie zou nog steeds thread-safe zijn, zelfs zonder de eerste nulcontrole. Dat zou echter betekenen dat een vergrendeling zou worden verkregen telkens wanneer de instantie wordt aangevraagd, en dat zou tot prestaties leiden. De eerste nulcontrole wordt toegevoegd zodat het slot niet wordt verkregen tenzij het nodig is. De tweede nulcontrole zorgt ervoor dat alleen de eerste thread die het slot verkrijgt, vervolgens de instantie maakt. De andere threads vinden het exemplaar dat moet worden ingevuld en gaan vooruit.
Luie, draadveilige Singleton (met Lazy )
.Net 4.0 type Lazy garandeert draad-veilige objectinitialisatie, dus dit type kan worden gebruikt om Singletons te maken.
public class LazySingleton
{
private static readonly Lazy<LazySingleton> _instance =
new Lazy<LazySingleton>(() => new LazySingleton());
public static LazySingleton Instance
{
get { return _instance.Value; }
}
private LazySingleton() { }
}
Als u Lazy<T>
zorgt u ervoor dat het object alleen wordt geïnstantieerd wanneer het ergens in de aanroepcode wordt gebruikt.
Een eenvoudig gebruik zal zijn als:
using System;
public class Program
{
public static void Main()
{
var instance = LazySingleton.Instance;
}
}
Luie, thread-veilige singleton (voor .NET 3.5 of ouder, alternatieve implementatie)
Omdat je in .NET 3.5 en ouder geen Lazy<T>
-klasse hebt, gebruik je het volgende patroon:
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();
}
}
Dit is geïnspireerd op de blogpost van Jon Skeet .
Omdat de Nested
klasse is genest en private tot het starten van de singleton instantie zal niet worden veroorzaakt door de toegang tot andere leden van de Sigleton
klasse (zoals een openbaar readonly eigendom, bijvoorbeeld).
De Singleton-instantie weggooien wanneer deze niet langer nodig is
De meeste voorbeelden tonen het instantiëren en vasthouden van een LazySingleton
object totdat de eigen toepassing is beëindigd, zelfs als dat object niet langer nodig is voor de toepassing. Een oplossing hiervoor is om IDisposable
te implementeren en de objectinstantie als volgt op nul te zetten:
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;
}
De bovenstaande code verwijdert het exemplaar voorafgaand aan de beëindiging van de toepassing, maar alleen als consumenten na elk gebruik Dispose()
op het object aanroepen. Aangezien er geen garantie is dat dit zal gebeuren of een manier om dit te forceren, is er ook geen garantie dat de instantie ooit zal worden verwijderd. Maar als deze klasse intern wordt gebruikt, is het gemakkelijker om ervoor te zorgen dat de methode Dispose()
na elk gebruik wordt aangeroepen. Een voorbeeld volgt:
public class Program
{
public static void Main()
{
using (var instance = LazySingleton.Instance)
{
// Do work with instance
}
}
}
Let op: dit voorbeeld is niet thread-safe .