wake-up-neo.com

Singleton mit Parametern

Ich brauche eine Singleton-Klasse, um mit einigen Argumenten instanziiert zu werden. Ich mache es jetzt so:

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...
     } 
}

Die Instanz wird "spät" erstellt, was bedeutet, dass beim Start der App nicht alle erforderlichen Argumente vorhanden sind.

Generell mag ich keine Reihenfolge von Methodenaufrufen erzwingen, aber ich sehe hier keinen anderen Weg. Der IoC würde es auch nicht beheben, da ich ihn auch im Container registrieren kann, kann ich auch Create () aufrufen ... 

Halten Sie dies für ein OK-Szenario? Hast du eine andere Idee? 

edit : Ich weiß, dass das, was ich als Beispiel geschrieben habe, nicht threadsicher ist, threadsicher ist nicht Teil der Frage

31
veljkoz

Ein Singleton mit Parametern riecht nach Fisch. 

Betrachten Sie die Antwort von whateva und den folgenden Code:

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

Natürlich arbeiten x == y und y mit den Erstellungsparametern von x, während die Erstellungsparameter von y einfach ignoriert werden. Die Ergebnisse sind wahrscheinlich ... verwirrend.

Wenn Sie wirklich gefallen sind, als müssten Sie es tun, machen Sie es so:

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);             
     } 
}

Fügen Sie in einer Multithreading-Umgebung eine Synchronisation hinzu, um Race-Bedingungen zu vermeiden.

22

Singleton ist hässlich, aber da der Benutzer whateva nicht die Mühe hat, seinen eigenen Code zu korrigieren ...

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 hat vor Jahren über diesen Blog gebloggt, denke ich, es ist ziemlich zuverlässig. Es sind keine Ausnahmen erforderlich, Sie müssen sich nicht daran erinnern, welche Objekte Singletons sein sollen, und den Fallout bearbeiten, wenn Sie einen Fehler feststellen. 

Bearbeiten: Die Typen sind nicht relevant, verwenden Sie das, was Sie möchten, object wird hier nur zur Vereinfachung verwendet.

31
annakata

Bessere Antwort:

  1. Erstellen Sie eine Schnittstelle: ISingleton (mit den gewünschten Aktionen)

  2. Und dein Typ: Singleton : ISingleton

  3. Angenommen, Sie haben Zugriff auf einen UnityContainer:

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

  1. Wenn Sie bereit sind, Ihre Typverwendung zu erstellen (vorausgesetzt, Sie verwenden Unity for DI):

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

  1. Wenn Sie den Singleton greifen möchten, verwenden Sie einfach:

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

Hinweis: Wenn für den Container kein Typ für die ISingleton-Schnittstelle registriert ist, sollte er entweder eine Ausnahme auslösen oder null zurückgeben.

Alte Antwort:

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;
    }
}

Ich würde mit etwas Ähnlichem gehen (Sie müssen möglicherweise überprüfen, ob die Instanz ebenfalls erstellt wurde), oder, wenn Ihr DI-Container das Auslösen von Ausnahmen bei nicht registrierten Typen unterstützt, würde ich dies tun.

ATTN: Nicht threadsicherer Code :)

6
Bogdan Maxim

Die Single Locking-Lösung mit Doppelsperrfunktion von annakata funktioniert nicht auf allen Plattformen. Es gibt einen Fehler in diesem Ansatz, der gut dokumentiert ist. Verwenden Sie diesen Ansatz nicht, sonst werden Sie Probleme bekommen.

Die einzige Möglichkeit, dieses Problem zu lösen, ist die Verwendung des volatilen Schlüsselworts, z.

private static volatile Singleton m_instance = null;

Dies ist der einzige Thread-sichere Ansatz.

2
Johnny

Wenn Sie .NET 4 (oder höher) verwenden, können Sie den System.Lazy-Typ ..__ verwenden. Er wird sich um das Thread-Sicherheitsproblem kümmern und faul sein, damit Sie keine Instanz unnötig erstellen. Auf diese Weise ist der Code kurz und sauber.

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; } }
}
1
yohait
/// <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;
    }
}
0
Brick

Ich kann in Ihrem Code eigentlich kein Singleton sehen. Verwenden Sie eine statische, getaktete getInstance-Methode, die das Singleton zurückgibt und es erstellt, wenn es noch nicht verwendet wurde.

0
DaVinci