J'ai besoin d'une classe singleton pour être instanciée avec quelques arguments. Voici comment je le fais:
class SingletonExample
{
private SingletonExample mInstance;
//other members...
private SingletonExample()
{
}
public SingletonExample Instance
{
get
{
if (mInstance == null)
{
throw new Exception("Object not created");
}
return mInstance;
}
}
public void Create(string arg1, string arg2)
{
mInstance = new SingletonExample();
mInstance.Arg1 = arg1;
mInstance.ObjectCaller = new ObjectCaller(arg2);
//etc... basically, create object...
}
}
L'instance est créée 'en retard', ce qui signifie que je n'ai pas tous les arguments nécessaires au démarrage de l'application.
En général, je n'aime pas forcer la commande des appels de méthode, mais je ne vois pas d'autre moyen ici. L'IoC ne le résoudrait pas non plus, car là où je peux l'enregistrer dans le conteneur, je peux aussi appeler Create () ...
Considérez-vous cela comme un scénario acceptable? Avez-vous une autre idée?
edit : Je sais que ce que j'ai écrit à titre d'exemple n'est pas thread-safe, thread-safe ne fait pas partie de la question
Un Singleton avec des paramètres sent le poisson pour moi.
Considérons la réponse de whateva et le code suivant:
Singleton x = Singleton.getInstance("hello", "world");
Singleton y = Singleton.getInstance("foo", "bar");
Évidemment, x == y et y fonctionnent avec les paramètres de création de x, tandis que les paramètres de création de y sont simplement ignorés. Les résultats sont probablement… déroutants au moins.
Si vous êtes vraiment, vraiment, comme vous devez le faire, procédez comme suit:
class SingletonExample
{
private static SingletonExample mInstance;
//other members...
private SingletonExample()
{ // never used
throw new Exception("WTF, who called this constructor?!?");
}
private SingletonExample(string arg1, string arg2)
{
mInstance.Arg1 = arg1;
mInstance.ObjectCaller = new ObjectCaller(arg2);
//etc... basically, create object...
}
public static SingletonExample Instance
{
get
{
if (mInstance == null)
{
throw new Exception("Object not created");
}
return mInstance;
}
}
public static void Create(string arg1, string arg2)
{
if (mInstance != null)
{
throw new Exception("Object already created");
}
mInstance = new SingletonExample(arg1, arg2);
}
}
Dans un environnement multithreading, ajoutez une synchronisation pour éviter les conditions de concurrence.
Singleton est moche, mais depuis l'utilisateur whateva ne peut pas être dérangé pour corriger son propre code ...
public class Singleton
{
private static Singleton _instance = null;
private static Object _mutex = new Object();
private Singleton(object arg1, object arg2)
{
// whatever
}
public static Singleton GetInstance(object arg1, object arg2)
{
if (_instance == null)
{
lock (_mutex) // now I can claim some form of thread safety...
{
if (_instance == null)
{
_instance = new Singleton(arg1, arg2);
}
}
}
return _instance;
}
}
Skeet a écrit sur son blog il y a quelques années, je pense, c'est assez fiable. Aucune exception nécessaire, vous ne devez pas vous rappeler quels objets sont supposés être des singletons ni gérer les retombées lorsque vous vous trompez.
Éditer: les types ne sont pas pertinents, utilisez ce que vous voulez, object
est simplement utilisé ici pour plus de commodité.
La solution singleton à double verrouillage fournie par annakata ne fonctionnera pas à chaque fois sur toutes les plateformes. il y a une faille dans cette approche qui est bien documentée. Ne pas utiliser cette approche ou vous allez vous retrouver avec des problèmes.
Le seul moyen de résoudre ce problème consiste à utiliser le mot clé volatile, par exemple.
private static volatile Singleton m_instance = null;
C'est la seule approche thread-safe.
Si vous utilisez .NET 4 (ou une version ultérieure), vous pouvez utiliser le type System.Lazy . Il s'occupera du problème de la sécurité des threads et le fera paresseux pour ne pas créer d'instance inutilement ..__ De cette façon, le code est court et propre.
public sealed class Singleton
{
private static readonly Lazy<Singleton> lazy =
new Lazy<Singleton>(() => new Singleton(),LazyThreadSafetyMode.ExecutionAndPublication);
private Singleton() { }
public static Singleton Instance { get { return lazy.Value; } }
}
/// <summary> Generic singleton with double check pattern and with instance parameter </summary>
/// <typeparam name="T"></typeparam>
public class SingleObject<T> where T : class, new()
{
/// <summary> Lock object </summary>
private static readonly object _lockingObject = new object();
/// <summary> Instance </summary>
private static T _singleObject;
/// <summary> Protected ctor </summary>
protected SingleObject()
{
}
/// <summary> Instance with parameter </summary>
/// <param name="param">Parameters</param>
/// <returns>Instance</returns>
public static T Instance(params dynamic[] param)
{
if (_singleObject == null)
{
lock (_lockingObject)
{
if (_singleObject == null)
{
_singleObject = (T)Activator.CreateInstance(typeof(T), param);
}
}
}
return _singleObject;
}
}
En fait, je ne vois pas de singleton dans votre code . Utilisez une méthode getInstance statique et paramétrée qui renvoie le singleton et le crée s'il n'a pas déjà été utilisé.