![Singleton](/images/patterns/cards/singleton-mini.png?id=914e1565dfdf15f240e766163bd303ec)
Singleton en C#
Le Singleton est un patron de conception de création qui s’assure de l’existence d’un seul objet de son genre et fournit un unique point d’accès vers cet objet.
Le singleton possède à peu près les mêmes avantages et inconvénients que les variables globales. Même s’ils sont super utiles, ils réduisent la modularité du code.
Vous ne pourrez pas utiliser une classe qui dépend d’un singleton dans un autre contexte. Vous devrez également inclure complètement la classe Singleton dans votre code. En général, on se rend compte de cette limitation lorsque l’on crée des tests unitaires.
Complexité :
Popularité :
Exemples d’utilisation : Beaucoup de développeurs considèrent que le singleton est un antipatron. C’est pourquoi il est de moins en moins utilisé en C#.
Identification : Le singleton peut être reconnu par une méthode de création statique qui retourne le même objet en cache.
Singleton naïf
Il est très facile d’implémenter un singleton bâclé. Il suffit de cacher le constructeur et d’implémenter une méthode de création statique.
Cette même classe peut mal fonctionner dans un environnement multithread. Plusieurs threads vont pouvoir appeler la méthode de création simultanément et créer plusieurs instances de la classe Singleton.
Program.cs: Exemple conceptuel
using System;
namespace RefactoringGuru.DesignPatterns.Singleton.Conceptual.NonThreadSafe
{
// The Singleton class defines the `GetInstance` method that serves as an
// alternative to constructor and lets clients access the same instance of
// this class over and over.
// EN : The Singleton should always be a 'sealed' class to prevent class
// inheritance through external classes and also through nested classes.
public sealed class Singleton
{
// The Singleton's constructor should always be private to prevent
// direct construction calls with the `new` operator.
private Singleton() { }
// The Singleton's instance is stored in a static field. There there are
// multiple ways to initialize this field, all of them have various pros
// and cons. In this example we'll show the simplest of these ways,
// which, however, doesn't work really well in multithreaded program.
private static Singleton _instance;
// This is the static method that controls the access to the singleton
// instance. On the first run, it creates a singleton object and places
// it into the static field. On subsequent runs, it returns the client
// existing object stored in the static field.
public static Singleton GetInstance()
{
if (_instance == null)
{
_instance = new Singleton();
}
return _instance;
}
// Finally, any singleton should define some business logic, which can
// be executed on its instance.
public void someBusinessLogic()
{
// ...
}
}
class Program
{
static void Main(string[] args)
{
// The client code.
Singleton s1 = Singleton.GetInstance();
Singleton s2 = Singleton.GetInstance();
if (s1 == s2)
{
Console.WriteLine("Singleton works, both variables contain the same instance.");
}
else
{
Console.WriteLine("Singleton failed, variables contain different instances.");
}
}
}
}
Output.txt: Résultat de l’exécution
Singleton works, both variables contain the same instance.
Singleton thread-safe
Pour régler ce problème, vous devez synchroniser les threads lors de la première création de l’objet Singleton.
Program.cs: Exemple conceptuel
using System;
using System.Threading;
namespace Singleton
{
// This Singleton implementation is called "double check lock". It is safe
// in multithreaded environment and provides lazy initialization for the
// Singleton object.
class Singleton
{
private Singleton() { }
private static Singleton _instance;
// We now have a lock object that will be used to synchronize threads
// during first access to the Singleton.
private static readonly object _lock = new object();
public static Singleton GetInstance(string value)
{
// This conditional is needed to prevent threads stumbling over the
// lock once the instance is ready.
if (_instance == null)
{
// Now, imagine that the program has just been launched. Since
// there's no Singleton instance yet, multiple threads can
// simultaneously pass the previous conditional and reach this
// point almost at the same time. The first of them will acquire
// lock and will proceed further, while the rest will wait here.
lock (_lock)
{
// The first thread to acquire the lock, reaches this
// conditional, goes inside and creates the Singleton
// instance. Once it leaves the lock block, a thread that
// might have been waiting for the lock release may then
// enter this section. But since the Singleton field is
// already initialized, the thread won't create a new
// object.
if (_instance == null)
{
_instance = new Singleton();
_instance.Value = value;
}
}
}
return _instance;
}
// We'll use this property to prove that our Singleton really works.
public string Value { get; set; }
}
class Program
{
static void Main(string[] args)
{
// The client code.
Console.WriteLine(
"{0}\n{1}\n\n{2}\n",
"If you see the same value, then singleton was reused (yay!)",
"If you see different values, then 2 singletons were created (booo!!)",
"RESULT:"
);
Thread process1 = new Thread(() =>
{
TestSingleton("FOO");
});
Thread process2 = new Thread(() =>
{
TestSingleton("BAR");
});
process1.Start();
process2.Start();
process1.Join();
process2.Join();
}
public static void TestSingleton(string value)
{
Singleton singleton = Singleton.GetInstance(value);
Console.WriteLine(singleton.Value);
}
}
}
Output.txt: Résultat de l’exécution
FOO
FOO
Vous en voulez plus ?
Il y a plein d’autres variétés spéciales du singleton en C#. Jetez un œil à cette article (en anglais) pour en découvrir d’autres :