Abstrakte Basisklasse jeweils abgeleiteten Klassen zu zwingen Singleton zu sein

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

  •  27-09-2019
  •  | 
  •  

Frage

Wie kann ich eine abstrakte Klasse zu machen, dass jeder abgeleiteten Klassen zwingen soll Singleton sein? Ich verwende C #.

War es hilfreich?

Lösung

Wenn Sie Kompilierung Überprüfung erzwingen möchten, ist dies nicht möglich. Mit der Laufzeit überprüft, dies zu tun. Es ist nicht schön, aber es ist möglich. Hier ein Beispiel:

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

Andere Tipps

, die nicht funktionieren würde, weil die Singletons Bedürfnisse Somwhere einen statischen Zugang und das nicht gezwungen werden kann.

für singletonimplemention + Beispiele siehe: Implementierung der Singleton-Muster in C #

mit Singleton Mitteln private Konstruktoren. Aber Sie wissen, private Mitglieder können nicht vererbt werden. In C ++ gab es Vorlagen, so dass Sie einen Singleton aus einer Template-Klasse erstellen können. in C # gibt es keine Vorlagen, so dass Sie Ihre eigenen Konstrukteuren für jedes Singleton Sie haben zu schreiben.

Hier ist ein (hässlich) Weg, dies zu tun. Es könnte wahrscheinlich vereinfacht und verbessert werden, aber es ist mein erstes gehen sie an.

Die Idee ist es, zunächst die Basisklasse eine generische abstrakte Klasse zu machen (wie in den Kommentaren oben erwähnt), aber der Typ-Parameter gezwungen ist, von der Basisklasse selbst abzuleiten. Dies ermöglicht die Basisklasse der einzelne Instanz des abgeleiteten Typs zu handhaben. Beachten Sie alle abgeleiteten Klassen sollten abgedichtet werden, wie bei jedem Singletonklasse.

Als nächstes wird ein geschützter Konstruktor erlaubt, aber erforderlich, um eine Instanz einer Sonderklasse zu akzeptieren, SingletonKey, der eine modifizierte Singleton ist. Abgeleitete Klassen haben Zugriff auf die SingletonKey Klassendefinition, aber die Basisklasse behält private Kontrolle über die einzige erlaubte Instanz und damit über den Bau aller abgeleiteten Objekte.

Drittens ist die Basisklasse müssen in der Lage sein den Konstruktor der abgeleiteten Klasse zu nennen, aber das ist etwas schwierig. Der Compiler wird sich beschweren, wenn Sie versuchen, den abgeleiteten Schlüssel Konstruktor aufzurufen, da es nicht garantiert ist zu existieren. Die Lösung ist ein statisch Delegierten hinzuzufügen, dass die abgeleitete Klasse initialisiert werden muss. So dass jeder abgeleiteten Klassen müssen eine einfache Initialisierung Verfahren bereitzustellen. Diese Initialisierung Methode explizit die Instanz zum ersten Mal in Code für den Zugriff werden soll, bevor oder ein Laufzeitfehler zur Folge hat.

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

Ich glaube, ich versuchte, etwas ähnliches zu erreichen, das heißt eine gemeinsame Schnittstelle durchzusetzen und die Singletonmuster auf eine Gruppe von Klassen. Dies war meine Lösung:

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

Hier ist ein einfacher 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();
    }
}

Bitte beachten Sie, dass Sie bequem die gemeinsame Schnittstelle von den generischen abstrakten Singletonklasse entfernen können, wenn Sie nur die grundlegende „Vorlage“ benötigen.

Hier ist meine Implementierung von Singleton Vererbung:

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";
    }
}
scroll top