Абстрактный базовый класс, чтобы заставить каждого полученных классов быть синглтоном

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

  •  27-09-2019
  •  | 
  •  

Вопрос

Как создать абстрактный класс, который должен заставить каждые полученные классы быть синглтоном? Я использую C #.

Это было полезно?

Решение

Когда вы хотите, чтобы проверить время компиляции, это невозможно. С проверкой выполнения вы можете сделать это. Это не красиво, но это возможно. Вот пример:

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

Другие советы

Это не будет работать, потому что синглтон нуждается в созревении статического доступа, и которые не могут быть вынуждены.

Для Singletonimplemention + примеры см.: Реализация рисунка Singleton в C #

Singleton означает наличие частных конструкторов. Но вы знаете, что частные члены не могут быть унаследованы. В C ++ были шаблоны, поэтому вы можете создать синглтон из класса шаблона. В C # не шаблоны, поэтому вы должны написать свои собственные частные конструкторы для каждого Wingleton, который вы хотите.

Вот (уродливый) способ сделать это. Вероятно, это может быть упрощено и улучшено, но это мой первый пойти на него.

Идея состоит в том, чтобы сначала сделать базовый класс универсальным абстрактным классом (как упомянуто в комментариях выше), но параметр типа ограничен для получения самого базового класса. Это позволяет базовому классу обрабатывать экземпляр Singleton производного типа. Обратите внимание, что все полученные классы должны быть запечатаны, как и с любым классом Singleton.

Далее разрешен защищенный конструктор, но требуется принять экземпляр специального класса, Singletonkey, который является модифицированным Singleton. Полученные классы имеют доступ к определению класса Singletonkey, но базовый класс сохраняет частный контроль над единственным допустимым экземпляром и, таким образом, по конструкции всех производных объектов.

В-третьих, базовый класс должен будет позвонить конструктору полученного класса, но это немного сложно. Компилятор будет жаловаться, если вы попытаетесь вызвать производный конструктор ключа, поскольку он не гарантирован. Решение состоит в том, чтобы добавить статический делегат, который полученный класс должен инициализировать. Таким образом, любые полученные классы должны обеспечить простую метод инициализации. Этот метод инициализации следует явно называться, прежде чем пытаться получить доступ к экземпляру в первый раз в коде, или ошибка времени выполнения будет результатом.

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

Классы в Java или C # не являются «первым классом». Статическая часть класса не может быть унаследована или переопределена подклассами. Видеть Этот ответ Больше подробностей. Кроме того, у вас нет понятия мета-класса.

На языках, таких как SmallTalk или Ruby, вы можете определить новый метакласс Singleton который определяет метод getInstance. Отказ Тогда вы можете определить ClassA и ClassB быть экземплярами Singleton метакласс. Тогда оба класса автоматически подвергают методу getInstance который можно использовать для создания экземпляров objectA или objectB. Отказ Разве это не круто? Ну, на практике вы часто не используете метакласс, а синглтон на самом деле является единственным использованием их, которые имеют смысл и что я знаю.

Я полагаю, что пытался достичь чего-то подобного, то есть обеспечить общий интерфейс и синглтон на группе классов. Это было мое решение:

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

Вот простой тест:

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

Обратите внимание, что вы можете легко удалить общий интерфейс от универсального абстрактного класса 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";
    }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top