Classe de base abstraite pour forcer chaque classes dérivées être Singleton

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

  •  27-09-2019
  •  | 
  •  

Question

Comment puis-je faire une classe abstraite qui forcerons chaque classes dérivées à être Singleton? J'utilise C #.

Était-ce utile?

La solution

Lorsque vous souhaitez appliquer la vérification de compilation, cela est impossible. Avec l'exécution, vous pouvez le faire vérifier. Ce n'est pas assez, mais il est possible. Voici un exemple:

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

Autres conseils

qui ne fonctionnerait pas parce que les besoins singleton Somwhere un accès statique et qui ne peuvent pas être forcés.

pour singletonimplemention + exemples voir: mise en œuvre du modèle Singleton en C #

moyens Singleton ayant les constructeurs privés. Mais vous savez que les membres privés ne peuvent être héritées. En C ++, il y avait des modèles, vous pouvez créer un singleton d'une classe de modèle. en C #, il n'y a pas de modèles, de sorte que vous devez écrire vos propres constructeurs privés pour chaque singleton que vous voulez.

Voici un (laid) façon de le faire. Il pourrait probablement être simplifié et amélioré, mais il est mon premier rendez-vous dans ce domaine.

L'idée est d'abord de faire la classe de base une classe abstraite générique (comme mentionné dans les commentaires ci-dessus), mais le paramètre de type est contraint de dériver de la classe de base elle-même. Cela permet à la classe de base pour gérer l'instance singleton du type dérivé. Notez toutes les classes dérivées doivent être scellés, comme toute classe singleton.

Ensuite, un constructeur protégé est autorisé, mais il est tenu d'accepter une instance d'une classe spéciale, SingletonKey, qui est un singleton modifié. Les classes dérivées ont accès à la définition de la classe SingletonKey, mais la classe de base conserve le contrôle privé sur l'instance seulement permis, et donc sur la construction de tous les objets dérivés.

En troisième lieu, la classe de base devra être en mesure d'appeler le constructeur de la classe dérivée, mais cela est un peu délicat. Le compilateur se plaindra si vous essayez d'appeler le constructeur à clé dérivée, car il n'est pas garanti d'exister. La solution est d'ajouter un délégué statique que la classe dérivée doit initialiser. Ainsi, les classes que leurs dérivés devront fournir une méthode simple d'initialisation. Cette méthode d'initialisation doit être appelé explicitement avant de tenter d'accéder à l'instance pour la première fois dans le code, ou une erreur d'exécution entraînera.

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

Les classes en Java ou C # ne sont pas "de première classe". La partie statique d'une classe ne peut pas être héritée ou remplacée par les sous-classes. Voir cette réponse pour plus de détails. De plus, vous ne disposez pas d'un concept de méta-classe.

Dans les langues comme Smalltalk ou Ruby, vous pouvez définir une nouvelle Singleton metaclass qui définit une méthode getInstance. Ensuite, vous pouvez définir ClassA et ClassB des cas de la métaclasse Singleton. Ensuite, les deux classes exposent automatiquement une getInstance méthode qui peut être utilisé pour créer des instances objectA ou objectB. Est-ce pas cool? Eh bien, en pratique, vous n'utilisez pas métaclasse fréquemment, et le singleton est en fait la seule utilisation d'entre eux qui fait sens et que je suis au courant.

Je crois que j'ai essayé d'obtenir quelque chose de similaire à savoir appliquer une interface commune et le motif de singleton sur un groupe de classes. Ce fut ma solution:

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

Voici un test simple:

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

S'il vous plaît noter que vous pouvez facilement supprimer l'interface commune de la classe abstraite singleton générique si vous avez besoin juste le « modèle » de base.

Voici ma mise en œuvre de l'héritage Singleton:

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";
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top