2010-11-17 44 views
23

Necesito una clase singleton para crear una instancia con algunos argumentos. La forma en que estoy haciendo ahora es:Singleton con los parámetros

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

la instancia se crea 'tarde', lo que significa que no tengo todos los argumentos necesarios en el arranque de aplicaciones.

En general, no me gusta forzar un pedido de llamadas a métodos, pero no veo otra manera aquí. El IoC tampoco lo resolvería, ya que donde puedo registrarlo en el contenedor, también puedo llamar a Crear() ...

¿Considera que es un buen escenario? ¿Tienes alguna otra idea?

edición: Yo sé que lo que escribí como un ejemplo que no es hilo de seguridad, hilo de seguridad no es parte de la pregunta

+0

que es * not * a singleton. ¿Dónde está la exclusión mutua? (es decir, 'bloqueo'). además su 'getter' para la instancia debería estar creando la instancia, no el ctor, ese es el punto. – RPM1984

+0

Estoy de acuerdo con RPM1984, este no es un singleton. ¿Pueden cambiar sus argumentos o serán los mismos durante la vigencia de su aplicación? Un singleton no debería depender de algo variable, podría depender de la configuración u otro singleton. Sin embargo, piense detenidamente en su diseño y pregúntese si necesita un singleton. Sobre el uso de singletons es realmente malo para su diseño. –

+1

Es singleton, créeme :) el 'getter' no puede crear la instancia ya que no tiene argumentos, podría haberlo hecho, pero es más conveniente escribirlo así. Y, los argumentos, una vez obtenidos, no cambian. – veljkoz

Respuesta

18

un producto único con parámetros huele mal a mí.

Consideremos la respuesta de whateva y el siguiente código:

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

Obviamente, x == obras Y e Y con los parámetros de creación de x, mientras que los parámetros de creación de Y son simplemente ignorados. Los resultados son probablemente ... confusos al menos.

Si realmente, realmente sentir como si tiene que hacerlo, hacerlo de esta manera:

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

En un entorno multihilo, añadir la sincronización para evitar condiciones de carrera.

+6

-1 No estoy de acuerdo con la solución porque creo que está sugiriendo utilizar el patrón de singleton donde no se debe usar. un singleton es una clase que solo permite crear una sola instancia de sí mismo, y generalmente da acceso simple a esa instancia. Si se debe acceder a la misma instancia para todas las solicitudes con el mismo parámetro, el patrón de fábrica es apropiado. En esta solución, puede llamar a Create (.....) tantas veces como desee para romper la inmutabilidad de la clase. –

+6

Massimiliano, en mi solución puedes llamar a crear exactamente una vez. Y creo que mis mensajes de texto dejan en claro que tampoco me gusta la idea de los singleton paramétricos. –

+0

@Massimiliano Peluso - ahora ** que ** es una observación útil - una fábrica ... lo que busco es en realidad una mezcla de singleton/fábrica, que en realidad se reduce a IoC ... De todos modos, creo que tengo mi respuesta ahora. Gracias – veljkoz

0

En realidad no puedo ver un producto único en su código. Utilice un método getInstance estático y parametrizado que devuelve el singleton y lo crea si no se utilizó antes.

4

Mejor respuesta:

  1. crear una interfaz: ISingleton (que contiene cualquier acción que desea que haga a)

  2. Y su tipo: Singleton : ISingleton

  3. suponiendo que tiene acceso a una UnityContainer:

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

  1. Cuando esté listo para crear su utilización tipo (suponiendo que está utilizando la Unidad de DI):

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

  1. Si quiere agarrar el singleton sólo tiene que utilizar:

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

Nota: Si el contenedor no tiene un tipo registrado para la interfaz ISingleton, entonces debe arrojar una excepción, o devolver nulo.

respuesta antigua:

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

me gustaría ir con algo similar (lo que pueda necesitar para comprobar si la instancia se creó también), o, si su contenedor DI soporta tirar excepción de los tipos no registrados, lo haría ir con eso.

Atención: no hilo de código seguro :)

+0

¿dónde creas una instancia de la clase? –

+0

gracias por señalar el error tipográfico –

22

Singleton es feo pero desde whateva usuario no puede ser molestado en corregir su propio código ...

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 blogs sobre esto hace años creo, es bastante fiable. Sin excepciones, no está en el negocio de recordar qué objetos se supone que son simples y manejar las consecuencias cuando se equivoca.

Editar: los tipos no son relevantes, utilice lo que desee, object se acaba de utilizar aquí para mayor comodidad.

+0

Ahora, ESO es un singleton, no es que los use explícitamente (dejo que DI los haga, y solo para cosas como el registro). +1 – RPM1984

+0

Voy a seguir adelante y asumir que whateva decidió rechazar una respuesta funcionalmente correcta. Vamos equipo. – annakata

+0

no, no lo hizo (mira su perfil). de todos modos, toda esta charla de singleton me ha dado sueño, me voy a la cama. :) – RPM1984

2

La solución singleton de doble bloqueo provista por annakata no funcionará siempre en todas las plataformas. hay un error en este enfoque que está bien documentado. No use este enfoque o terminará con problemas.

La única forma de resolver este problema es utilizar la palabra clave volátil, p.

private static volatile Singleton m_instance = null; 

Este es el único enfoque seguro de subprocesos.

2

Si está usando .NET 4 (o superior), puede utilizar el tipo System.Lazy. Cuidará el problema de la seguridad del hilo y lo hará perezoso para que no cree una instancia innecesariamente. De esta manera, el código es corto y limpio.

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