2010-05-18 10 views

Respuesta

2

Cuando desee adelantar la verificación del tiempo de compilación, esto no es posible. Con la comprobación del tiempo de ejecución puede hacer esto. No es bonito, pero es posible. Aquí hay un ejemplo:

public abstract class Singleton 
{ 
    private static readonly object locker = new object(); 
    private static HashSet<object> registeredTypes = new HashSet<object>(); 

    protected Singleton() 
    { 
     lock (locker) 
     { 
      if (registeredTypes.Contains(this.GetType())) 
      { 
       throw new InvalidOperationException(
        "Only one instance can ever be registered."); 
      } 
      registeredTypes.Add(this.GetType()); 
     } 
    } 
} 

public class Repository : Singleton 
{ 
    public static readonly Repository Instance = new Repository(); 

    private Repository() 
    { 
    } 
} 
+1

Eso no es un singleton. Con el patrón singleton, su llamada a 'getInstance' siempre tendrá éxito. Su proposición es de hecho solo una verificación en tiempo de ejecución que no proporciona ninguno de los beneficios del patrón singleton. – ewernli

5

que no funcionaría porque el singleton necesita un acceso estático y no se puede forzar.

para ejemplos singletonimplemention + ver: Implementing the Singleton Pattern in C#

+0

el artículo es realmente brillante. Llegué a la conclusión de que esto no será posible, pero sigo buscando ideas. –

+1

Para referencia, el enlace actualizado es: http://csharpindepth.com/Articles/General/Singleton.aspx – Seph

2

Singleton significa tener constructores privados. Pero sabes que los miembros privados no pueden ser heredados. En C++ había plantillas, por lo que podía crear un singleton a partir de una clase de plantilla. en C#, no hay plantillas, por lo que debe escribir sus propios constructores privados para cada singleton que desee.

1

Las clases en java o C# no son de "primera clase". La parte estática de una clase no puede ser heredada o anulada por subclases. Ver this answer para más detalles. Además, no tienes un concepto de metaclase.

En idiomas como Smalltalk o Ruby, puede definir una nueva metaclase Singleton que define un método getInstance. Luego puede definir ClassA y ClassB instancias de la metaclass Singleton. Entonces ambas clases exponen automáticamente un método getInstance que se puede usar para crear instancias objectA o objectB. ¿No es genial? Bueno, en la práctica no usas metaclass con frecuencia, y el singleton es en realidad el único uso de ellos que tiene sentido y de lo que soy consciente.

1

Aquí hay una forma (fea) de hacer esto. Probablemente podría simplificarse y mejorarse, pero es mi primer intento.

La idea es primero hacer que la clase base sea una clase abstracta genérica (como se menciona en los comentarios anteriores), pero el parámetro de tipo está obligado a derivar de la clase base misma. Esto permite que la clase base maneje la instancia singleton del tipo derivado. Tenga en cuenta que todas las clases derivadas deben estar selladas, como con cualquier clase singleton.

A continuación, se permite un constructor protegido, pero se requiere para aceptar una instancia de una clase especial, SingletonKey, que es un singleton modificado. Las clases derivadas tienen acceso a la definición de clase SingletonKey, pero la clase base conserva el control privado sobre la única instancia permitida y, por lo tanto, sobre la construcción de todos los objetos derivados.

En tercer lugar, la clase base tendrá que ser capaz de llamar al constructor de la clase derivada, pero esto es un poco complicado. El compilador se quejará si intentas llamar al constructor con clave derivada, ya que no se garantiza que exista. La solución es agregar un delegado estático que la clase derivada debe inicializar. Por lo tanto, cualquier clase derivada deberá proporcionar un método de inicialización simple. Este método de inicialización debe invocarse explícitamente antes de intentar acceder a la instancia por primera vez en el código, o se producirá un error de tiempo de ejecución.

public abstract class Singleton<T> where T : Singleton<T> 
{ 
    protected Singleton(SingletonKey key) { } 
    private static SingletonKey _key; 
    private static SingletonKey Key 
    { 
     get 
     { 
      if (_key == null) SingletonKey.Initialize(); 
      return _key; 
     } 
    } 
    protected class SingletonKey 
    { 
     private SingletonKey() 
     { 
     } 
     public static void Initialize() 
     { 
      if (_key == null) 
      { 
       _key = new SingletonKey(); 
      } 
     } 
    } 

    protected static Func<SingletonKey, T> Creator; 
    private static T instance; 
    public static T Instance 
    { 
     get 
     { 
      if (instance == null) instance = Creator(Key); 
      return instance; 
     } 
    } 
} 

public class MySingleton : Singleton<MySingleton> 
{ 
    public string Name { get; set; } 
    public static void Initialize() 
    { 
     Creator = (key) => new MySingleton(key); 
    } 
    protected MySingleton(SingletonKey key) : base(key) 
    { 
    } 
} 
0

Creo que he tratado de lograr algo similar es decir, hacer cumplir una interfaz común y el patrón Singleton en un grupo de clases.Este fue mi solución:

// Common interface of my singleton classes 
public interface IMySingletonClass 
{ 
    string ValueGetter(); 

    void ValueSetter(string value); 
} 

// Generic abstract base class 
public abstract class Singleton<T>: IMySingletonClass 
{ 
    private static readonly object instanceLock = new object(); 
    private static T instance; // Derived class instance 

    // Protected constructor accessible from derived class 
    protected Singleton() 
    { 
    } 

    // Returns the singleton instance of the derived class 
    public static T GetInstance() 
    { 
     lock (instanceLock) 
     { 
      if (instance == null) 
      { 
       instance = (T)Activator.CreateInstance(typeof(T), true); 
      } 
      return instance; 
     } 
    } 

    // IMySingletonClass interface methods 
    public abstract string ValueGetter(); 

    public abstract void ValueSetter(string value); 
} 

// Actual singleton class 
public class MySingletonClass : Singleton<MySingletonClass> 
{ 
    private string myString; 

    private MySingletonClass() 
    { 
     myString = "Initial"; 
    } 

    public override string ValueGetter() 
    { 
     return myString; 
    } 

    public override void ValueSetter(string value) 
    { 
     myString = value; 
    } 
} 

Aquí es una prueba sencilla:

class Program 
{ 
    static void Main(string[] args) 
    { 
     MySingletonClass r1 = MySingletonClass.GetInstance(); 
     Console.WriteLine("R1 value = {0}", r1.ValueGetter()); 
     r1.ValueSetter("Changed through R1"); 
     MySingletonClass r2 = MySingletonClass.GetInstance(); 
     Console.WriteLine("R2 value = {0}", r2.ValueGetter()); 

     Console.ReadKey(); 
    } 
} 

Tenga en cuenta que se puede quitar fácilmente la interfaz común de la clase Singleton abstracta genérica si sólo tiene la "plantilla" básica .

Cuestiones relacionadas