2011-04-04 10 views
6

Esto es una ligera variación de esta pregunta: Possible to use a singleton with a non-default constructor in C#?¿Es posible usar un singleton con un constructor no predeterminado en C#?

Tengo una clase que toma los parámetros para su constructor. Me gustaría hacer este singleton de modo que los parámetros se tomen al inicializar el singleton y, por lo tanto, no necesitarían pasarse cada vez que se recupere la instancia.

Mi solución (que no es elegante) para esto es tener un método estático CreateInstance() que toma los parámetros y construye la instancia singleton. Entonces tendría otro método estático GetInstance() que sería sin parámetros para obtener la instancia singleton. En el código, necesitaría garantizar que la lógica llame al CreateInstance antes de cualquier llamada a GetInstance. Sin embargo, no puedo aplicar esto en tiempo de compilación. Puedo, sin embargo, comprobar en tiempo de ejecución al lanzar una excepción en GetInstance si se llama antes de CreateInstance.

¿Hay alguna forma de conseguir este comportamiento con la aplicación de tiempo de compilación? O al menos, ¿hay una mejor manera de hacer lo mismo?

+0

@ user691226 ¿Podría usted algún código de cómo ha hecho esto? ya, puedo tener algunos consejos. – msarchet

Respuesta

5

No hay forma de hacerlo en tiempo de compilación, porque sería como preguntar al compilador "¿pueden probar que el código X nunca se ejecuta antes de que se ejecute el código Y, en presencia de múltiples hilos?". No se puede hacer.

En cuanto al comportamiento de ejecución de su diseño, creo que esto es tan bueno como puede ser nunca.

Usted puede hacer que sea un poco mejor mediante la exposición de una propiedad Func<SingletonType> en su clase Singleton. Cuando alguien solicita la instancia de singleton y la instancia aún no se ha creado, su clase llamaría a este "método de fábrica" ​​para construir el singleton. Si el método de fábrica es null, entonces o bien lanzar una excepción o (si es aplicable) construyen utilizando algunos parámetros por defecto.

Lo que esto hace es esencialmente diferir la construcción del singleton hasta que realmente se necesita por primera vez, por lo que es alguna mejora. Pero el principio subyacente es el mismo.

Actualización:

Como LukeH puntos, esto es más o menos lo hace Lazy<T> (.NET 4 solamente). Si es posible, utilícelo en lugar de escribir el suyo.

+0

Gracias por la respuesta. Supongo que esperaba haber perdido una forma completamente diferente de lograr el mismo comportamiento que me da la verificación del tiempo de compilación. – millie

+2

Ha descrito más o menos cómo funciona 'Lazy ': http://msdn.microsoft.com/en-us/library/dd642331.aspx – LukeH

+0

@LukeH: Gracias por la sugerencia, nunca lo he usado así lejos. – Jon

0

Puede simplemente tener GetInstance() llamar al método CreateInstance() si el objeto singleton ya no existe.

+1

No puede hacer esto porque GetInstance no tiene los parámetros que CreateInstance necesita. – millie

+0

Puedes determinar lo que debería ser – msarchet

0

lo haría similar a este. Puede que tenga que añadir cerraduras u otras cosas para asegurar:

public class ClassA {  
    private static ClassA instance; 

    private int param; 

    private ClassA(int param) { 
     this.param = param; 
    } 

    public static ClassA getInstance() { 
     if (instance == null) { 
      throw new CustomException("Not yet initialised"); 
     } else { 
      return instance; 
     } 
    } 

    public static void createInstance(int param) { 
     if (instance == null) { 
      instance = new ClassA(param); 
     } 
    } 
} 
+1

Sí, este es el código que ya tengo. Desafortunadamente, la pregunta era si puedo cambiar la ejecución de este comportamiento en tiempo de ejecución para compilar el comportamiento del tiempo. Pero no parece posible. – millie

+0

Oh, lo siento, he entendido mal su pregunta. Pero no, no puedes. Creo que esta es más o menos la única forma de hacerlo. –

0

En el método de GetInstance(), ¿por qué no llamas a CreateInstance si su valor es nulo, entonces usted tiene la inicialización perezosa ..

+0

Porque necesita pasar argumentos a la función 'CreateInstance'. Esto no proporciona una solución a la pregunta que se hizo. –

+0

De dónde obtiene estos argumentos mágicos si no se pasan a GetInstance, ya que ninguno se pasa a GetInstance, supuse que se conocerían en este punto ... –

+0

La pregunta ya aborda esto: * "Mi solución (que no es elegante)) para esto es tener un método estático CreateInstance() que toma los parámetros y construye la instancia de singleton. Tendría entonces otro método estático GetInstance() que sería parameterless para obtener la instancia de singleton. "* –

1

En un conjunto unitario clásico, el magia real sucede en static readonly que crea la instancia tan pronto como se utiliza:

public class MySingleton 
{ 
    private static readonly _instance = new MySingleton(); 

    private MySingleton() {} 

    public static MySingleton Instance 
    { 
     get 
     { 
      return _instance; 
     } 
    } 

} 

Si tiene parámetros para pasar al constructor, usted tiene que poner en práctica encerrarse (nótese el doble if sandwitching la lock): ret

public class MySingletonWithConstructor 
{ 
    private static _instance; 
    private static object _lock = new Object(); 

    private MySingletonWithConstructor(string myArg) 
    { 
     // ... do whatever necessary 
    } 

    public static MySingletonWithConstructor Instance 
    { 
     get 
     { 
      if(_instance==null) 
      { 
       lock(_lock) 
       { 
        if(_instance==null) // double if to prevent race condition 
        { 
         _instance = new MySingletonWithConstructor("Something"); 
        } 
       } 
      } 
      return _instance; 
     } 
    } 

} 
+0

+1 Agradable ... Nunca hubiera pensado en ese doble constructo 'si', y me tomó aproximadamente un minuto comprenderlo. Simple y efectivo. – corlettk

+0

esto es una construcción totalmente inútil porque si el getter de instancia singleton siempre pasa "Algo" (o algún otro valor definido internamente) entonces 'myArg' no está funcionando como un parámetro - al menos no tan lejos como a los consumidores - tan efectivamente esta clase todavía tiene un constructor sin parámetros – BaltoStar

0

uso CreateInstance() para ser el cargador de un Lazy<T> y tienen GetInstance urn the Lazy.Value (es posible que desee crear un campo de solo lectura estático que esté establecido en = thelazy.Value para asegurar una sola entrada en CreateInstance())

Cuestiones relacionadas