Реализация наследуемого синглтон-класса в C#
Я обнаружил, что в C# вы можете реализовать класс Singleton следующим образом:
class Singleton
{
private static Singleton _instance;
public static Singleton Instance
{
get { return _instance ?? (_instance = new Singleton()); }
}
protected Singleton() { }
}
Который работает для экземпляров типа Singleton
т.е.
var a = Singleton.Instance;
var b = Singleton.Instance;
Console.WriteLine(ReferenceEquals(a, b)); //Prints True.
Но что, если я хочу, чтобы производные классы Singleton также следовали шаблону Singleton, то есть:
class A:Singleton
{ ... }
A a = A.Instance;
В этом случае статический член Instance
доступ к Singleton
класс и создает Singleton
пример, который не является целью. Кроме того, есть два основных проблемы с этим решением:
- Производный класс может реализовать свой собственный конструктор и потерять шаблон Singleton.
- Если есть другой случай
Singleton
тогда производный класс будет ссылаться на этот менее производный экземпляр
Мой вопрос: есть ли другой способ реализовать класс Singleton в C#, гарантирующий, что производный класс также будет singleton?
2 ответа
Не обращая внимания на обычное "Не используйте Singleton, посмотрите на свой дизайн". аргументы, вы могли бы реализовать его следующим образом (при условии, что у ваших производных классов есть конструкторы по умолчанию):
public abstract class Singleton<T> where T : class, new()
{
private static T _instance;
public static T GetInstance()
{
if(_instance == null)
_instance = new T();
return _instance;
}
}
И выведем таким образом:
public class SingletonA : Singleton<SingletonA> { /* .... */ }
public class SingletonB : Singleton<SingletonB> { /* .... */ }
Но я действительно не защищаю этот синглтонный подход лично. У них есть свое (редкое) применение, но они могут оказаться более болезненными для задницы - превращаясь в прославленные глобальные переменные контейнеры.
Кроме того, помните о безопасности потоков.
Мой вопрос: есть ли другой способ реализовать класс Singleton в C#, гарантирующий, что производный класс также будет singleton?
Ну, вы можете иметь какую-то проверку внутри конструктора, которая:
- Фактический тип
this
запечатан - Фактический тип
this
является прямым подклассомSingleton
- Никакой другой экземпляр этого типа не был создан (сохраняя
HashSet<Type>
)
Однако это кажется довольно бессмысленным. Чего на самом деле должен достичь ваш базовый класс?
Одноэлементный шаблон легко реализовать правильно (чего, кстати, нет в вашем примере - он не безопасен для потоков), так зачем иметь базовый класс? Сам базовый класс не будет одноэлементным (потенциально может быть много экземпляров - по одному на каждый подкласс), так в чем же выгода?
Мне кажется, что "я одноэлементный для фактического типа объекта", во-первых, не является подходящей основой для наследования, и, честно говоря, я бы в любом случае старался избегать паттерна-одиночки.
Если вам действительно нужен базовый класс, это должно быть потому, что есть некоторые общие функциональные возможности, которые естественным образом наследуют все подклассы. Это вряд ли связано с тем, являются ли эти подклассы одиночными.