C# Language
Mise en œuvre singleton
Recherche…
Singleton statiquement initialisé
public class Singleton
{
private readonly static Singleton instance = new Singleton();
private Singleton() { }
public static Singleton Instance => instance;
}
Cette implémentation est thread-safe car dans ce cas instance
objet instance
est initialisé dans le constructeur statique. Le CLR garantit déjà que tous les constructeurs statiques sont exécutés en mode thread-safe.
L' instance
mutation n'est pas une opération thread-safe, par conséquent l'attribut readonly
garantit l'immuabilité après l'initialisation.
Singleton paresseux et thread-safe (à l'aide du verrouillage à double contrôle)
Cette version thread-safe d'un singleton était nécessaire dans les premières versions de .NET où static
initialisation static
n'était pas garantie pour être thread-safe. Dans les versions plus modernes du framework, un singleton statiquement initialisé est généralement préféré car il est très facile de faire des erreurs d'implémentation dans le modèle suivant.
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;
}
}
}
Notez que la vérification if (instance == null)
est effectuée deux fois: une fois avant l’acquisition du verrou et une fois par la suite. Cette implémentation serait toujours thread-safe même sans la première vérification NULL. Cependant, cela signifierait qu'un verrou serait acquis à chaque fois que l'instance est demandée, ce qui pourrait nuire à la performance. La première vérification NULL est ajoutée afin que le verrou ne soit pas acquis, sauf si nécessaire. La deuxième vérification NULL garantit que seul le premier thread à acquérir le verrou crée alors l'instance. Les autres threads trouveront l'instance à remplir et passeront à la suite.
Paresseux, Singleton (utilisant Lazy )
Le type .Net 4.0 Lazy garantit l'initialisation d'objet thread-safe, donc ce type pourrait être utilisé pour créer 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() { }
}
Utiliser Lazy<T>
fera en sorte que l'objet ne soit instancié que lorsqu'il est utilisé quelque part dans le code d'appel.
Un usage simple sera comme:
using System;
public class Program
{
public static void Main()
{
var instance = LazySingleton.Instance;
}
}
Démo en direct sur .NET Fiddle
Paresseux, singleton thread-safe (pour .NET 3.5 ou version antérieure, implémentation alternative)
Parce que dans .NET 3.5 et versions antérieures, vous n'avez pas la classe Lazy<T>
, vous utilisez le modèle suivant:
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();
}
}
Ceci est inspiré du post du blog de Jon Skeet .
Parce que le Nested
l'instanciation de l'instance singleton classe est imbriquée privé et ne sera pas déclenchée en accédant à d' autres membres de la Sigleton
classe (comme une propriété publique en lecture seule, par exemple).
Élimination de l'instance Singleton lorsqu'elle n'est plus nécessaire
La plupart des exemples montrent l'instanciation et la conservation d'un objet LazySingleton
jusqu'à la fin de l'application propriétaire, même si l'application n'a plus besoin de cet objet. Une solution consiste à implémenter IDisposable
et à définir l'instance d'objet sur null comme suit:
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;
}
Le code ci-dessus dispose de l'instance avant la fin de l'application, mais uniquement si les consommateurs appellent Dispose()
sur l'objet après chaque utilisation. Comme il n'y a aucune garantie que cela se produira ou une manière de le forcer, rien ne garantit que l'instance sera jamais éliminée. Mais si cette classe est utilisée en interne, il est plus facile de s’assurer que la méthode Dispose()
est appelée après chaque utilisation. Un exemple suit:
public class Program
{
public static void Main()
{
using (var instance = LazySingleton.Instance)
{
// Do work with instance
}
}
}
Veuillez noter que cet exemple n'est pas compatible avec les threads .