2010-11-17 20 views
23

J'ai besoin d'une classe singleton pour être instanciée avec certains arguments. La façon dont je le fais maintenant:Singleton avec paramètres

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éé « fin », 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 une commande d'appels de méthode, mais je ne vois pas d'autre moyen ici. L'IoC ne le résoudrait pas non plus, puisque je peux l'enregistrer dans le conteneur, je peux aussi appeler Create() ...

Considérez-vous cela comme un bon scénario? Avez-vous une autre idée?

modifier: Je sais que ce que j'ai écrit comme un exemple, il est pas thread-safe, thread-safe ne fait pas partie de la question

+0

qui n'est pas un singleton. où est l'exclusion mutuelle? (c'est-à-dire 'lock'). plus votre 'getter' pour l'instance devrait être la création de l'instance, pas le ctor - c'est tout le point. – RPM1984

+0

Je suis d'accord avec RPM1984, ce n'est pas un singleton. Vos arguments peuvent-ils changer ou seront-ils les mêmes pendant toute la durée de votre demande? Un singleton ne devrait pas dépendre de quelque chose de variable, cela pourrait dépendre de la configuration ou d'un autre singleton. Cependant, réfléchissez bien à votre conception et demandez-vous si c'est un singleton dont vous avez besoin. Plus d'utiliser singletons est vraiment mauvais pour votre conception. –

+1

C'est bien singleton, croyez-moi :) le 'getter' ne peut pas créer l'instance car il n'a pas d'arguments - il aurait pu, mais c'est plus pratique de l'écrire comme ça. Et, les arguments, une fois obtenus ne changent pas. – veljkoz

Répondre

18

A Singleton avec des paramètres me sent le poisson.

Tenir compte de la réponse whateva et le code suivant:

Singleton x = Singleton.getInstance("hello", "world"); 
Singleton y = Singleton.getInstance("foo", "bar"); 

De toute évidence, x == œuvres y et y avec les paramètres de création de x, tandis que les paramètres de création de y sont tout simplement ignorés. Les résultats sont probablement ... déroutants au moins.

Si vous avez vraiment, vraiment tombé comme vous devez le faire, le faire comme ceci:

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, ajouter la synchronisation pour éviter les conditions de course.

+6

-1 Je ne suis pas d'accord avec la solution parce que je pense que vous suggérez d'utiliser le modèle de singleton où il ne devrait pas être utilisé. un singleton est une classe qui ne permet qu'une seule instance de lui-même à créer, et donne généralement un accès simple à cette instance Si la même instance doit être accessible pour toutes les demandes avec le même paramètre, le modèle d'usine est approprié. peut appeler Créer (.....) autant de fois que vous voulez rompre l'immutabilité de la classe. –

+6

Massimiliano, dans ma solution, vous pouvez appeler créer exactement une fois. Et je pense que mon texto est suffisamment clair pour que je n'aime pas vraiment l'idée de singletons paramétrés. –

+0

@Massimiliano Peluso - maintenant ** que ** est une observation utile - une usine ... ce que je suis après est en fait un mélange de singleton/usine, qui se résume à IoC ... En tout cas, je pense que j'ai ma réponse maintenant. Merci – veljkoz

0

Je ne peux pas vraiment voir un 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 été utilisé auparavant.

4

Meilleure réponse:

  1. Créer une interface: ISingleton (contenant toutes les actions que vous voulez faire pour)

  2. Et votre type: Singleton : ISingleton

  3. En supposant que vous avez accès à un UnityContainer:

IUnityContainer _singletonContainer = new UnityContainer(); // or whatever code to initialize the container

  1. Lorsque vous êtes prêt à créer votre utilisation de type (en supposant que vous utilisez l'unité pour DI):

_singletonContainer.RegisterType(typeof(ISingleton), new Singleton(params));

  1. Si vous voulez saisir le singleton il suffit d'utiliser:

var localSingletonVar = _singletonContainer.Resolve<ISingleton>();

Remarque: Si le conteneur n'a pas de type enregistré pour l'interface ISingleton, il doit soit renvoyer une exception, soit renvoyer une valeur nulle.

Old Réponse:

public class Singleton 
{ 

    private static Singleton instance = null; 

    private Singleton(String arg1, String arg2) 
    { 
    } 

    public static Singleton getInstance(String arg1, String arg2) 
    { 
     if (instance != null) 
     { 
      throw new InvalidOperationException("Singleton already created - use getinstance()"); 
     } 
     instance = new Singleton(arg1, arg2); 
     return instance; 
    } 

    public static Singleton getInstance() 
    { 
     if (instance == null) 
      throw new InvalidOperationException("Singleton not created - use GetInstance(arg1, arg2)"); 
     return instance; 
    } 
} 

je partirais avec quelque chose de similaire (vous pourriez avoir besoin de vérifier si instance a été créée aussi), ou, si votre conteneur DI prend en charge lancer exception sur les types non enregistrés, je aller avec ça.

ATTN: fil non Code sécurité :)

+0

où créez-vous une instance de la classe ??? –

+0

merci de pointer la faute de frappe –

22

Singleton est laid, mais depuis whateva utilisateur ne peut pas être pris la peine de 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 blogué il y a environ cette année, je pense, il est assez fiable. Aucune exception n'est nécessaire, vous n'avez pas à vous rappeler quels objets sont censés être des singletons et à gérer les retombées lorsque vous vous trompez. Editer: les types ne sont pas pertinents, utilisez ce que vous voulez, object est juste utilisé ici pour plus de commodité.

+0

Maintenant, c'est un singleton, pas que je les utilise explicitement (je laisse DI les faire, et seulement pour des choses comme la journalisation). +1 – RPM1984

+0

Je vais aller de l'avant et supposer que whateva a décidé de downvote une réponse fonctionnellement correcte. Allez l'équipe. – annakata

+0

non, il n'a pas (regardez son profil). Quoi qu'il en soit, tout ce discours de singleton m'a fait dormir, je me couche. :) – RPM1984

2

La solution de double singlet à verrouillage fournie par annakata ne fonctionnera pas à chaque fois sur toutes les plates-formes. il y a une faille dans cette approche qui est bien documentée. N'utilisez pas cette approche ou vous vous retrouverez avec des problèmes.

La seule façon de résoudre ce problème consiste à utiliser le mot-clé volatile par ex.

private static volatile Singleton m_instance = null; 

Ceci est la seule approche thread-safe.

2

Si vous utilisez .NET 4 (ou supérieur), vous pouvez utiliser le type System.Lazy. Il va prendre soin de vous le problème de sécurité des threads et le faire paresseux de sorte que vous ne créerez pas 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; } } 
}