Classe base astratta per forzare ciascuna classe derivata ad essere Singleton

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

  •  27-09-2019
  •  | 
  •  

Domanda

Come posso creare una classe astratta che imponga a ciascuna classe derivata di essere Singleton?Io uso C#.

È stato utile?

Soluzione

Quando si desidera applicare il controllo fase di compilazione, questo non è possibile. Con runtime verifica si può fare questo. Non è bello, ma è possibile. Ecco un esempio:

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()
    {
    }
}

Altri suggerimenti

che non avrebbe funzionato perché le esigenze singleton somwhere un accesso statico e che non possono essere forzati.

per singletonimplemention + esempi vedere: attuazione del pattern Singleton in C #

Singleton significa avere costruttori privati.Ma sai che i membri privati ​​non possono essere ereditati.In C++ c'erano dei modelli, quindi potevi creare un singleton da una classe modello.in C# non ci sono modelli, quindi devi scrivere i tuoi costruttori privati ​​per ogni singleton che desideri.

Ecco un modo (brutto) per fare questo. Si potrebbe probabilmente essere semplificato e migliorato, ma è la mia prima andare a esso.

L'idea è di rendere prima classe base una classe astratta generica (come menzionato in osservazioni di cui sopra), ma il parametro tipo è costretto a derivare dalla classe base stessa. Questo permette alla classe di base per gestire l'istanza singleton di tipo derivato. Nota tutte le classi derivate devono essere sigillati, come con qualsiasi classe Singleton.

Avanti, un costruttore protetto è consentito, ma è obbligato ad accettare un'istanza di una classe speciale, SingletonKey, che è un Singleton modificato. Le classi derivate hanno accesso alla definizione di classe SingletonKey, ma la classe di base mantiene il controllo privato sopra l'istanza solo permesso, e quindi sulla costruzione di tutti gli oggetti derivati.

In terzo luogo, la classe di base dovrà essere in grado di chiamare il costruttore della classe derivata, ma questo è un po 'complicato. Il compilatore si lamenterà se si tenta di chiamare il costruttore con chiave derivata, dal momento che non è garantito di esistere. La soluzione è quella di aggiungere un delegato statica che la classe derivata deve inizializzare. Così le classi derivate qualsiasi dovranno fornire un semplice metodo di inizializzazione. Questo metodo di inizializzazione deve essere chiamato in modo esplicito prima di tentare di accedere all'istanza per la prima volta nel codice, o comporteranno un errore di runtime.

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

Corsi in Java o C # non sono "di prima classe". La parte statica di una classe non può essere ereditata o sovrascritto dalle sottoclassi. Vedere questa risposta per ulteriori dettagli. Inoltre, non si dispone di un concetto di meta-class.

In linguaggi come Smalltalk o Ruby, è possibile definire un nuovo Singleton metaclasse che definisce un metodo getInstance. Poi si può definire ClassA e ClassB essere casi del metaclasse Singleton. Poi entrambe le classi espongono automaticamente un getInstance metodo che può essere utilizzato per creare istanze objectA o objectB. Non è che cool? Beh, in pratica non si utilizza metaclasse di frequente, e il Singleton è in realtà l'unico utilizzo di loro che abbia un senso e che io sappia.

Credo che ho cercato di realizzare qualcosa di simile vale a dire far rispettare un'interfaccia comune e il pattern Singleton su un gruppo di classi. Questa è stata la mia soluzione:

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

Ecco un semplice test:

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

Si prega di notare che si può facilmente rimuovere l'interfaccia comune dalla generica classe singleton astratta se avete solo bisogno il "modello" di base.

Ecco mia implementazione di Singleton eredità:

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";
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top