C# Language
Singleton-Implementierung
Suche…
Statisch initialisiertes Singleton
public class Singleton
{
private readonly static Singleton instance = new Singleton();
private Singleton() { }
public static Singleton Instance => instance;
}
Diese Implementierung ist threadsicher , da in diesem Fall instance
Objekt im statischen Konstruktor initialisiert. Die CLR stellt bereits sicher, dass alle statischen Konstruktoren threadsicher ausgeführt werden.
Das Ändern der instance
ist keine Thread-sichere Operation, daher garantiert das readonly
Attribut die Unveränderlichkeit nach der Initialisierung.
Faules, fadensicheres Singleton (mit Double Checked Locking)
Diese Thread-sichere Version eines Singleton wurde in den frühen .NET-Versionen benötigt, bei denen die static
Initialisierung nicht garantiert Thread-sicher war. In moderneren Versionen des Frameworks wird normalerweise ein statisch initialisierter Singleton bevorzugt, da es sehr einfach ist, Implementierungsfehler im folgenden Muster zu machen.
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;
}
}
}
Beachten Sie, dass die Prüfung if (instance == null)
zweimal durchgeführt wird: einmal vor dem Sperren und einmal danach. Diese Implementierung wäre auch ohne die erste Nullprüfung Thread-sicher. Dies würde jedoch bedeuten, dass jedes Mal, wenn die Instanz angefordert wird, eine Sperre erlangt wird , was die Performance beeinträchtigen würde. Die erste Nullprüfung wird hinzugefügt, damit die Sperre nicht erfasst wird, sofern dies nicht erforderlich ist. Die zweite Nullüberprüfung stellt sicher, dass nur der erste Thread, der die Sperre erhält, die Instanz erstellt. Die anderen Threads finden die zu füllende Instanz und springen voraus.
Faules, fadensicheres Singleton (mit Lazy )
Der .NET 4.0-Typ Lazy garantiert eine Thread-sichere Objektinitialisierung, sodass dieser Typ für Singletons verwendet werden könnte.
public class LazySingleton
{
private static readonly Lazy<LazySingleton> _instance =
new Lazy<LazySingleton>(() => new LazySingleton());
public static LazySingleton Instance
{
get { return _instance.Value; }
}
private LazySingleton() { }
}
Mit Lazy<T>
wird sichergestellt, dass das Objekt nur instanziiert wird, wenn es irgendwo im aufrufenden Code verwendet wird.
Eine einfache Verwendung wird wie folgt sein:
using System;
public class Program
{
public static void Main()
{
var instance = LazySingleton.Instance;
}
}
Fauler, threadsicherer Singleton (für .NET 3.5 oder älter, alternative Implementierung)
Da Sie in .NET 3.5 und älter keine Lazy<T>
-Klasse haben, verwenden Sie das folgende Muster:
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();
}
}
Dies ist inspiriert von Jon Skeets Blogpost .
Da die Nested
Klasse verschachtelt und privat ist, wird die Instantiierung der Singleton-Instanz nicht durch den Zugriff auf andere Mitglieder der Sigleton
Klasse (z. B. eine öffentliche readonly-Eigenschaft) ausgelöst.
Entsorgen der Singleton-Instanz, wenn sie nicht mehr benötigt wird
Die meisten Beispiele zeigen, wie ein LazySingleton
Objekt instanziiert und gehalten wird, bis die LazySingleton
beendet wurde, selbst wenn dieses Objekt von der Anwendung nicht mehr benötigt wird. Eine Lösung hierfür ist, IDisposable
zu implementieren und die Objektinstanz wie folgt auf null zu setzen:
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;
}
Der obige Code gibt die Instanz vor der Anwendungsbeendigung frei, aber nur, wenn die Verbraucher nach jeder Verwendung Dispose()
für das Objekt aufrufen. Da es keine Garantie dafür gibt, dass dies geschehen wird, gibt es auch keine Garantie, dass die Instanz jemals beseitigt wird. Wenn diese Klasse jedoch intern verwendet wird, ist es einfacher sicherzustellen, dass die Dispose()
-Methode nach jeder Verwendung aufgerufen wird. Ein Beispiel folgt:
public class Program
{
public static void Main()
{
using (var instance = LazySingleton.Instance)
{
// Do work with instance
}
}
}
Bitte beachten Sie, dass dieses Beispiel nicht threadsicher ist .