clase base abstracta para obligar a cada uno de las clases derivadas para ser Singleton

StackOverflow https://stackoverflow.com/questions/2855245

  •  27-09-2019
  •  | 
  •  

Pregunta

¿Cómo hago una clase abstracta que obligarán a cada uno las clases derivadas para ser Singleton? Yo uso C #.

¿Fue útil?

Solución

Cuando se quiere hacer cumplir la comprobación en tiempo de compilación, esto no es posible. Con tiempo de ejecución de la comprobación se puede hacer esto. No es bonito, pero es posible. He aquí 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()
    {
    }
}

Otros consejos

que no funcionaría porque las necesidades simples Somwhere un acceso estática y que no puede ser forzado.

para singletonimplemention + ejemplos ver: de aplicación del patrón Singleton en C #

medios Singleton tener constructores privados. Pero sabes miembros privados no pueden ser heredados. En C ++ hubo plantillas, por lo que podría crear un producto único de una clase de plantilla. en C #, no son las plantillas, lo que tiene que escribir sus propios constructores privados para cada producto único que desea.

Aquí está una manera (feo) para hacer esto. Probablemente se podría simplificar y mejorar, pero es mi primera ir en él.

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

A continuación, se permite que 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 de base mantiene el control privado sobre la instancia sólo se permite, 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 difícil. El compilador se quejará si intenta llamar al constructor con llave derivada, ya que no se garantiza que existe. La solución es añadir un delegado estática que la clase derivada debe inicializar. Así clases cualquier derivadas tendrán que proporcionar un método de inicialización simple. Este método de inicialización se debe llamar de forma explícita antes de intentar acceder a la instancia por primera vez en el código, o dará lugar a un error 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)
    {
    }
}

Las clases en Java o C # no son "de primera clase". La parte estática de una clase no puede ser heredada o anulado por subclases. Ver esta respuesta para más detalles. Además, usted no tiene un concepto de meta-clase.

En lenguajes como Smalltalk o Ruby, puede definir un nuevo Singleton metaclase que define un método getInstance. A continuación, puede definir ClassA y ClassB haber casos de la metaclase Singleton. Entonces ambas clases exponen automáticamente un getInstance método que puede ser usado para crear instancias objectA o objectB. ¿No es genial? Pues bien, en la práctica no se utiliza de metaclase con frecuencia, y el singleton es en realidad el único uso de ellos que tenga sentido y que yo sepa.

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

Esta 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 genérica abstracta si sólo tiene la "plantilla" básico.

Aquí mi aplicación de Singleton herencia:

using System;
using System.Reflection;

namespace Mik.Singleton
{
    class Program
    {
        static void Main()
        {
            //You can not create an instance of class directly
            //Singleton1 singleton1 = new Singleton1();

            Singleton1 singleton1 = Singleton1.Instance;
            Singleton2 singleton2 = Singleton2.Instance;

            Console.WriteLine(singleton1.Singleton1Text);
            Console.WriteLine(singleton2.Singleton2Text);

            Console.ReadLine();
        }
    }

    public class SingletonBase<T> where T : class
    {
        #region Singleton implementation

        private static readonly object lockObj = new object();
        private static T _instance;

        protected SingletonBase() { }

        public static T Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (lockObj)
                    {
                        if (_instance == null)
                            _instance = CreateInstance();
                    }
                }
                return _instance;
            }
        }

        private static T CreateInstance()
        {
            ConstructorInfo constructor = typeof(T).GetConstructor(
                            BindingFlags.Instance | BindingFlags.NonPublic,
                            null, new Type[0],
                            new ParameterModifier[0]);

            if (constructor == null)
                throw new Exception(
                    $"Target type is missing private or protected no-args constructor: {typeof(T).FullName}");
            try
            {
                T instance = constructor.Invoke(new object[0]) as T;

                return instance;
            }
            catch (Exception e)
            {
                throw new Exception(
                    "Failed to create target: type=" + typeof(T).FullName, e);
            }
        }

        #endregion Singleton implementation
    }

    public class Singleton1 : SingletonBase<Singleton1>
    {
        private Singleton1() { }

        public string Singleton1Text { get; } = "Singleton1Text value";
    }

    public class Singleton2 : SingletonBase<Singleton2>
    {
        private Singleton2() { }

        public string Singleton2Text { get; } = "Singleton2Text value";
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top