Доступ к унаследованным членам от статических методов - Singleton Inheritance?

Краткое изложение проблемы

На самом деле я просто хочу использовать обычную функциональность наследования.
Суть в том, что некоторые из моих методов являются статическими, поэтому они не могут получить доступ к унаследованным свойствам, как обычно. Кроме того, я не могу ничего передать статическим методам (например, экземпляру объекта), потому что они вызываются "внешними триггерами".
С другой стороны, мне не нужно многократные экземпляры, то есть что-то вроде синглтона было бы хорошо.

Позвольте мне объяснить вам мою ситуацию шаг за шагом:
(Или просто перейдите к первому примеру кода, чтобы увидеть пример MCV)

Требование 1 - Статические методы доступа к членам

Я использую библиотеку Harmony для исправления методов существующей сборки. Методы исправления должны быть статическими и вызываться "хуками" библиотеки.
Я добавляю новые улучшения игрока в игру. Для каждого обновления, которое я хочу патчить, я создаю класс с членами, такими как имя, описание, изображение... Одно обновление должно быть пропатчено только один раз, так что решение здесь будет делать что-то статичное.
Но...

Требование 2 - Наследование

Поскольку в этих обновлениях используется много общих членов и методов, я создаю класс BaseUpgrade и извлекаю его из него.
Каждая конкретная реализация теперь может присваивать свои значения общим полям, таким как имя, описание... и наследовать остальные (методы, использующие члены).
Тем не менее, члены больше не доступны из статических методов исправления. Для этого я могу использовать Singletons.
Я тоже хочу унаследовать все вещи Singleton, сделав их общими. Таким образом, только базовый класс имеет весь код Singeton.
Одним из подходов было бы это решение: /questions/41725252/realizatsiya-nasleduemogo-singlton-klassa-v-c/41725257#41725257
Тем не мение...

Требование 3 - Наличие коллекции базового класса

Мне нужно иметь коллекции базового класса и использовать их в словарях (как ключ, так и значение). Но это не похоже на общие классы.
Я нашел коллекцию универсальных типов, но я застрял. Я не знаю, сработает ли это на самом деле. По крайней мере, это усложнит ситуацию еще больше.

Будет ли это работать? Может быть, есть гораздо более простой подход к моей проблеме?


MCV Пример базового сценария

using System;
using System.Collections.Generic;

namespace Using_Singleton
{

    // This is the version trying to incorperate the inheritable singleton
    class Base<T> where T : Base<T>, new()
    {
        #region Singleton stuff

        private static T _instance;

        public static T Instance
        {
            get
            {
                if (_instance == null)
                    _instance = new T();
                return _instance;
            }
            set => _instance = value;
        }

        #endregion

        public string name;     // Should be accessible by the derived class' static methods
        public string desc;

        protected Base()
        {
            name = "Base";
        }

        public void printName()
        {
            Console.WriteLine(name);
        }
    }

    class FirstChild : Base<FirstChild>
    {
        public int number;      // Should be accessible by the class' static methods

        public FirstChild()
        {
            name = "The first child";
            number = 7;
        }

        public static void StaticMethod_FirstChild()
        {
            Console.WriteLine("StaticMethod_FirstChild: I can access all member variables! :-)");
            Console.WriteLine("Name: " + Instance.name + ", Number: " + Instance.number);     // This is now working
        }

    }

    class SecondChild : Base<SecondChild>
    {
        public float myfloat;

        public SecondChild()
        {
            name = "The second child";
            myfloat = 0.3f;
        }

        public static void StaticMethod_SecondChild()
        {
            Console.WriteLine("StaticMethod_SecondChild: I can access all member variables! :-)");
            Console.WriteLine("Name 2x: " + Instance.name + " " + Instance.name);       // This is now working
        }
    }

    class Manager       // Manages instances/singletons which derive from "Base" by using a collection of the Base class
    {
        //Dictionary<string, Base> itemDict;        // ******* This is now broken

        public Manager()
        {
            //itemDict = new Dictionary<string, Base>();

            //Base addItem;

            //addItem = new FirstChild();
            //itemDict.Add(addItem.GetType().Name, addItem);

            //addItem = new SecondChild();
            //itemDict.Add(addItem.GetType().Name, addItem);

            // Simulating the external call of one static method
            SecondChild.StaticMethod_SecondChild();
            Console.WriteLine();
        }

        public void DoSomething()
        {
            //foreach (var item in itemDict)
            //{
            //  item.Value.printName();
            //}
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            Manager manager = new Manager();
            manager.DoSomething();

            Console.ReadLine();
        }
    }

}

Пример использования наследуемых синглетонов

using System;
using System.Collections.Generic;

namespace Using_Singleton
{

    // This is the version trying to incorperate the inheritable singleton
    class Base<T> where T : Base<T>, new()
    {
        #region Singleton stuff

        private static T _instance;

        public static T Instance
        {
            get
            {
                if (_instance == null)
                    _instance = new T();
                return _instance;
            }
            set => _instance = value;
        }

        #endregion

        public string name;     // Should be accessible by the derived class' static methods
        public string desc;

        protected Base()
        {
            name = "Base";
        }

        public void printName()
        {
            Console.WriteLine(name);
        }
    }

    class FirstChild : Base<FirstChild>
    {
        public int number;      // Should be accessible by the class' static methods

        public FirstChild()
        {
            name = "The first child";
            number = 7;
        }

        public static void StaticMethod_FirstChild()
        {
            Console.WriteLine("StaticMethod_FirstChild: I can access all member variables! :-)");
            Console.WriteLine("Name: " + Instance.name + ", Number: " + Instance.number);     // This is now working
        }

    }

    class SecondChild : Base<SecondChild>
    {
        public float myfloat;

        public SecondChild()
        {
            name = "The second child";
            myfloat = 0.3f;
        }

        public static void StaticMethod_SecondChild()
        {
            Console.WriteLine("StaticMethod_SecondChild: I can access all member variables! :-)");
            Console.WriteLine("Name 2x: " + Instance.name + " " + Instance.name);       // This is now working
        }
    }

    class Manager       // Manages instances/singletons which derive from "Base" by using a collection of the Base class
    {
        //Dictionary<string, Base> itemDict;        // ******* This is now broken

        public Manager()
        {
            //itemDict = new Dictionary<string, Base>();

            //Base addItem;

            //addItem = new FirstChild();
            //itemDict.Add(addItem.GetType().Name, addItem);

            //addItem = new SecondChild();
            //itemDict.Add(addItem.GetType().Name, addItem);

            // Simulating the external call of one static method
            SecondChild.StaticMethod_SecondChild();
            Console.WriteLine();
        }

        public void DoSomething()
        {
            //foreach (var item in itemDict)
            //{
            //  item.Value.printName();
            //}
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            Manager manager = new Manager();
            manager.DoSomething();

            Console.ReadLine();
        }
    }
}

1 ответ

Решение

После добавления Inheritable Singletons в небольшой пример я попытался добавить поддержку универсальной коллекции, как это предложил Джон Сондерс.

По сути, создание интерфейса или класса "над" одноэлементным классом (класс синглтона наследует его) и использование его для коллекции или в параметрах метода.

В этот новый неуниверсальный класс я поместил все поля и методы. Конструкторы верхнего неуниверсального класса и универсального класса защищены, поэтому они не могут быть созданы.

Рабочий раствор

using System;
using System.Collections.Generic;

namespace Using_Singleton_And_GenericCollection
{
    // This is the version trying to incorperate the inheritable singleton and a generic collection

    abstract class NonGenericBase       // Adding this (class or interface) make the use of collections possible.
    {
        public string name;
        public string desc;

        public void printName()
        {
            Console.WriteLine("\t" + name);
        }

        protected NonGenericBase() { }
    }

    class Base<T> : NonGenericBase where T : Base<T>, new()
    {
        #region Singleton stuff

        protected static T _instance;

        public static T Instance
        {
            get
            {
                if (_instance == null)
                    _instance = new T();
                return _instance;
            }
            set => _instance = value;
        }

        #endregion

        //public string name;     // Moved to parent
        //public string desc;

        protected Base()
        {
            name = "Base";
        }
    }


    class FirstChild : Base<FirstChild>
    {
        public int number;      // Should be accessible by the class' static methods

        public FirstChild()
        {
            name = "The first child";
            number = 7;
        }

        public static void StaticMethod_FirstChild()
        {
            Console.WriteLine("\tStaticMethod_FirstChild: I can access all member variables! :-)");
            Console.WriteLine("\tName: " + Instance.name + ", Number: " + Instance.number);     // This is now working
        }
    }

    class SecondChild : Base<SecondChild>
    {
        public float myfloat;

        public SecondChild()
        {
            name = "The second child";
            myfloat = 0.3f;
        }

        public static void StaticMethod_SecondChild()
        {
            Console.WriteLine("\tStaticMethod_SecondChild: I can access all member variables! :-)");
            Console.WriteLine("\tName 2x: " + Instance.name + ", " + Instance.name);       // This is now working
        }
    }


    class Manager       // Manages instances/singletons which derive from "Base" by using a collection of the Base class
    {
        public Dictionary<string, NonGenericBase> itemDict;

        public Manager()
        {
            itemDict = new Dictionary<string, NonGenericBase>();

            NonGenericBase addItem;

            addItem = FirstChild.Instance;
            itemDict.Add(addItem.GetType().Name, addItem);

            addItem = SecondChild.Instance;
            itemDict.Add(addItem.GetType().Name, addItem);
        }

        public void DoSomething()
        {
            foreach (var item in itemDict)
            {
                item.Value.printName();
            }
            Console.WriteLine();
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            var sec = new SecondChild();

            Console.WriteLine("Access Singletons");
            Manager manager = new Manager();
            manager.DoSomething();

            Console.WriteLine("Change Singletons");
            manager.itemDict[nameof(FirstChild)].name = "first name changed";
            SecondChild.Instance.name = "second name changed too";
            manager.DoSomething();

            Console.WriteLine("Create and change a non-Singleton instance of FirstChild");
            var initItem = new FirstChild();
            initItem.printName();
            initItem.name = "Non-Singleton name changed";
            initItem.printName();
            Console.WriteLine();

            Console.WriteLine("Access Singletons");
            manager.DoSomething();

            Console.WriteLine("Call static method of FirstChild");
            FirstChild.StaticMethod_FirstChild();         //Simulating the external call of one static method
            Console.WriteLine();

            Console.ReadKey();
        }
    }
} 

Выход

Access Singletons
        The first child
        The second child

Change Singletons
        first name changed
        second name changed too

Create and change a non-Singleton instance of FirstChild
        The first child
        Non-Singleton name changed

Access Singletons
        first name changed
        second name changed too

Call static method of FirstChild
        StaticMethod_FirstChild: I can access all member variables! :-)
        Name: first name changed, Number: 7

Предостережение

Из-за "new()" здесь

class Base<T> : NonGenericBase where T : Base<T>, new()

Конструкторы определенных подклассов должны быть открытыми. Это означает, что синглтоны не являются обязательными, а просто "опцией" (см. Пример для вывода).

BTownTKD заявляет, что в своем ответе он констатирует этот факт и связывает его с попыткой решить эту проблему с помощью отражения вызываемых вручную частных конструкторов здесь: Singleton.cs

Заключение

Благодаря тому, что я сделал пример MCV для этого вопроса и попробовал его снова со значительно меньшей сложностью, я нашел решение сам.
Таким образом, этот процесс опроса и улучшение моего начального поста помогли мне в этом.:-)

Другие вопросы по тегам