Доступ к унаследованным членам от статических методов - 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 для этого вопроса и попробовал его снова со значительно меньшей сложностью, я нашел решение сам.
Таким образом, этот процесс опроса и улучшение моего начального поста помогли мне в этом.:-)