Здравствуйте.
Подскажите, плиз, если кто в курсе, как реализовать такую вещь:
необходимо сделать множество классов синглтонов. Чтобы избавиться
от повторного кода, хочу вынести функциональность отвечающую за реализацию
синглтона в базовый класс.
class A // базовый класс
{
public static A GetInstance()
{}
protected A()
{}
}
class B : A // производный
{
private B()
{}
}
Возможно ли при вызовые B.GetInstance() в статическом методе GetInstance понять,
что тип класса не A а B, преобразовать его и создать новый экземпляр?
Здравствуйте, dancingintherain, Вы писали: D>Возможно ли при вызовые B.GetInstance() в статическом методе GetInstance понять, D>что тип класса не A а B, преобразовать его и создать новый экземпляр?
Рекомендую сделать так:
public class Singleton<T>
where T : Singleton<T>, new()
{
public static T Instance
{
get
{
return new T(); // заменить на код получения единственного экземпляра типа T.
}
}
}
Пользоваться, очевидно, вот так:
public class A : Singleton<A>
{
}
Сразу после этого появится возможность писать A.Instance.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, dancingintherain, Вы писали:
D>Подскажите, плиз, если кто в курсе, как реализовать такую вещь:
D>необходимо сделать множество классов синглтонов. Чтобы избавиться D>от повторного кода, хочу вынести функциональность отвечающую за реализацию D>синглтона в базовый класс.
Допустим, ситуация у вас такая:
abstract class X // Базовый класс для синглетонов
{
}
class A : X // конкретный синглетон A
{
private A() { }
}
class B : X // конкретный синглетон B
{
private B() { }
}
Тогда можно сделать примерно так:
class X
{
}
class X<Y> : X where Y : X<Y>
{
private static readonly Y instance = (Y)Activator.CreateInstance(typeof(Y), true);
public static Y Instance {
[DebuggerStepThrough]
get { return instance; }
}
}
sealed class A : X<A>
{
private A() { }
}
sealed class B : X<B>
{
private B() { }
}
Что бы избавиться от рефлекшена и оставить "реализацию" одиночки в одном месте, придётся сделать конструкторы A и B открытыми, что само по себе нарушает концепцию одиночки. Другой путь: не пытаться "обобщить" управление синглетонами и оставить дублирование:
abstract class X
{
}
class A : X
{
private static readonly A instance = new A();
private A() { }
public static A Instance {
[DebuggerStepThrough]
get { return instance; }
}
}
class B : X
{
private static readonly B instance = new B();
private B() { }
public static B Instance {
[DebuggerStepThrough]
get { return instance; }
}
}
ИМХО, это не то дублирование, от которого имеет смысл избавляться.
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, Sinclair, Вы писали:
D>>Возможно ли при вызовые B.GetInstance() в статическом методе GetInstance понять, D>>что тип класса не A а B, преобразовать его и создать новый экземпляр?
S>Рекомендую сделать так:
S>public class Singleton<T>
S> where T : Singleton<T>, new()
S>{
S> public static T Instance
S> {
S> get
S> {
S> return new T(); // заменить на код получения единственного экземпляра типа T.
S> }
S> }
S>}
А разве имеет смысл (если синглетоны вообще смысл имеют) делать открытый (то есть доступный всем) конструктор у синглетона? ИМХО, суть синглетона как раз в том, что _нельзя_ получить более одного экземпляра синглетона (не зря синглетонами называют именно _типы_ а не что-то ещё). Если эта суть нарушается, то такой синглетон превращается в самую обычную глобальную переменную Тогда уж точно можно (то есть лучше) обойтись любым DI-контейнером.
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
_FR>А разве имеет смысл (если синглетоны вообще смысл имеют) делать открытый (то есть доступный всем) конструктор у синглетона?
Ну почему сразу открытый.
_FR>ИМХО, суть синглетона как раз в том, что _нельзя_ получить более одного экземпляра синглетона (не зря синглетонами называют именно _типы_ а не что-то ещё).
Ну, насчёт "нельзя", это, конечно, неправда.
В том смысле, что если нельзя, но очень хочется, то никто не может помешать нам взять экземпляр, и через рефлекшн добраться до его конструктора.
Поэтому на практике достаточно обеспечить помощь компилятора в обеспечении единой точки доступа.
В самом простом случае все наследники Singleton<T> расположены в той же сборке, и достаточные гарантии можно получить при помощи internal конструктора.
_FR>Если эта суть нарушается, то такой синглетон превращается в самую обычную глобальную переменную Тогда уж точно можно (то есть лучше) обойтись любым DI-контейнером.
В более сложном случае можно сделать чуть более универсальное, но чуть менее удобное решение, например превратить Instance в public get; protected set;.
Тогда каждый потомок будет регистрировать инстанс в статическом конструкторе.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
_FR>>А разве имеет смысл (если синглетоны вообще смысл имеют) делать открытый (то есть доступный всем) конструктор у синглетона? S>Ну почему сразу открытый.
Этого требует констрейнт new().
_FR>>ИМХО, суть синглетона как раз в том, что _нельзя_ получить более одного экземпляра синглетона (не зря синглетонами называют именно _типы_ а не что-то ещё). S>Ну, насчёт "нельзя", это, конечно, неправда. S>В том смысле, что если нельзя, но очень хочется, то никто не может помешать нам взять экземпляр, и через рефлекшн добраться до его конструктора.
Это уже "хакерство", которое надо делать намеренно и которое можно запретить.
S>Поэтому на практике достаточно обеспечить помощь компилятора в обеспечении единой точки доступа.
Вот это не понял.
S>В самом простом случае все наследники Singleton<T> расположены в той же сборке, и достаточные гарантии можно получить при помощи internal конструктора.
Тогда придётся использовать reflection для создания экземпляров, который не всегда доступен и статически не контролируем.
_FR>>Если эта суть нарушается, то такой синглетон превращается в самую обычную глобальную переменную Тогда уж точно можно (то есть лучше) обойтись любым DI-контейнером. S>В более сложном случае можно сделать чуть более универсальное, но чуть менее удобное решение, например превратить Instance в public get; protected set;. S>Тогда каждый потомок будет регистрировать инстанс в статическом конструкторе.
Тогда уже ответственность за "синглетонизм" размазывается между предком и потомком. Во-вторых, propected set, который торчит наружу (для потомка) и доступен отовсюду так же не к месту.
Тогда можно забыть и о readonly полях, считая частью договорённости то, что некоторые поля не надо менять (через рефлекшен-то можно!) и о sealed классах, от которых тоже на самом деле при необходимости можно унаследоваься. Многие [и люди, и проекты] с этими договорённостями "в уме" нормально и долго существуют. Но не могут, в частности, ответить на вопрос "зачем же тогда было нужно вводить это в язык, если этим не пользоваться". Так же можно обойтись и двумя видами инкапсуляции — internal и public
Всё-таки чем меньше неявных соглашений, тем лучше. Если нам не нужны в принципе два экземпляра класса, значит надо делать конструктор закрытым, ибо если мы не делаем его таковым, надо быть готовым к тому, что его вызовут. Как только мы захотим добавить проверку в конструктор, не вызывался ли он уже, то должны будем понять уже, что делаем что-то противоестественное.
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, dancingintherain, Вы писали:
D>Подскажите, плиз, если кто в курсе, как реализовать такую вещь: D>необходимо сделать множество классов синглтонов.
"Как убить себя гранатой" (с)
"Не кладите яйца в микроволновку... "
Сделай отдельно фабрику, которая будет контролировать количество экземпляров нужных классов и создавай объекты через нее. Ну или действительно какой DI контейнер заиспользовать можно, если речь о множестве синглтонов идет.
Если нужно, чтобы никто другой до обычных конструкторов не добрался, то можно убрать все в отдельную сборку.
Здравствуйте, _FRED_, Вы писали: _FR>Этого требует констрейнт new().
Упс, точно.
_FR>Это уже "хакерство", которое надо делать намеренно и которое можно запретить.
Это ты про CAS — насчёт запрета?
Дело же не в том, чтобы совсем-совсем запретить. Дело в том, чтобы не дать сделать непреднамеренную ошибку.
_FR>Всё-таки чем меньше неявных соглашений, тем лучше. Если нам не нужны в принципе два экземпляра класса, значит надо делать конструктор закрытым, ибо если мы не делаем его таковым, надо быть готовым к тому, что его вызовут. Как только мы захотим добавить проверку в конструктор, не вызывался ли он уже, то должны будем понять уже, что делаем что-то противоестественное.
Ну, это, в каком-то смысле понятно. Ну а вывод какой? Я пока не вижу способа сделать магию на C#, которая бы при добавлении Singleton<Me> в список предков автоматом добавила бы конструирование экземпляра через приватный конструктор.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
_FR>>Это уже "хакерство", которое надо делать намеренно и которое можно запретить. S>Это ты про CAS — насчёт запрета?
Да.
_FR>>Всё-таки чем меньше неявных соглашений, тем лучше. Если нам не нужны в принципе два экземпляра класса, значит надо делать конструктор закрытым, ибо если мы не делаем его таковым, надо быть готовым к тому, что его вызовут. Как только мы захотим добавить проверку в конструктор, не вызывался ли он уже, то должны будем понять уже, что делаем что-то противоестественное.
S>Ну, это, в каком-то смысле понятно. Ну а вывод какой? Я пока не вижу способа сделать магию на C#, которая бы при добавлении Singleton<Me> в список предков автоматом добавила бы конструирование экземпляра через приватный конструктор.
Правильно не видишь. Такого способа нет. А вывод я делаю такой, что писать Singleton<> (или паразитного родителя как в моём примере) не нужно. Потому что эти способы смогут работать только или через открытый конструктор без параметров, что не совсем синглетону подходит, или через рефлекшен, что не очень "чисто". Ни один из возможных вариантов не может являться лучшим. То есть с синглетоном (если уж без него не обойтись) ничего "оптимизировать" в плане декларации\реализации не нужно: всегда в чём-то выиграем, но в что-то и упустим.
С другой стороны, писанины при явном объявлении статических поля и свойства для Instance не так уж и много. Если ею заниматься всё же лень, не сложно поле\свойство\конструктор (при необходимости) генерировать на этапе компиляции (T4, PostSharp и т.п.).
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, IB, Вы писали: IB>Сделай отдельно фабрику, которая будет контролировать количество экземпляров нужных классов и создавай объекты через нее. Ну или действительно какой DI контейнер заиспользовать можно, если речь о множестве синглтонов идет. IB>Если нужно, чтобы никто другой до обычных конструкторов не добрался, то можно убрать все в отдельную сборку.
Ну, собственно, тут как бы и напрашивается
1. Закрытие конструктора от неосторожных пользователей.
2. Замена его публичным статическим методом-фабрикой, который контролирует количество создаваемых экземпляров
Для одиночного случая всё более-менее очевидно. Для массового случая, когда таких классов много, возникает искушение отделить политику контроля количества экземпляров от оформления, а оформление — от реализации собственно конструктора.
Вопрос только в том, что всё зависит от самой политики. Скажем, WebRequest решает эту задачу существенно другим способом, чем SqlConnection. Поэтому общее решение, которое я предложил, не делает ничего полезного.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Вопрос только в том, что всё зависит от самой политики. Скажем, WebRequest решает эту задачу существенно другим способом, чем SqlConnection. Поэтому общее решение, которое я предложил, не делает ничего полезного.
Ну вот, не помню как там WebRequest, а с HttpContext и всем остальным семейством (HttpResponse/Request) ребята явно сделали что-то не то. Сначала соорудили синглтон, а потом, когда приперло, были вынуждены городить Http...Base/Http...Wrapper на каждый такой синглтон, чтобы это можно было хоть как-то тестировать.
Ровно по этому, эту фабрику лучше реализовывать не в базовом классе, а вообще где-нибудь с боку, не думаю, что это даст больше шансов ошибиться.
Здравствуйте, Пельмешко, Вы писали:
_FR>>... и о sealed классах, от которых тоже на самом деле при необходимости можно унаследоваься.
П>WTF? П>Поясните, пожалуйста, _FRED_. Опять какая-нибудь remoting-магия?
Мне кажется (где-то предавно читал), MSIL не запрещает наследоваться от sealed классов. Но сейчас как-то не получается в интернете отыскать подтверждения, так что, быть может, тут я ляпнул глупость. Emit тоже бьёт по рукам, так что наверное в данном пункте я наврал
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, dancingintherain, Вы писали:
D>необходимо сделать множество классов синглтонов. Чтобы избавиться от повторного кода, хочу вынести функциональность отвечающую за реализацию D>синглтона в базовый класс.
Неужто так много повторного кода?
Я всегда использую следующую конструкцию:
public sealed class Singleton
{
Singleton()
{
}
public static Singleton Instance
{
get
{
return Nested.instance;
}
}
class Nested
{
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinitstatic Nested()
{
}
internal static readonly Singleton instance = new Singleton();
}
}
Здравствуйте, _FRED_, Вы писали:
_FR>Тогда можно сделать примерно так:
_FR>
_FR> class X<Y> : X where Y : X<Y>
_FR> {
_FR> private static readonly Y instance = (Y)Activator.CreateInstance(typeof(Y), true);
_FR> public static Y Instance {
_FR> [DebuggerStepThrough]
_FR> get { return instance; }
_FR> }
_FR> }
_FR>
Спасибо, это именно то, о чем я спрашивал.
Но при виде этого, мне хочется воспользоваться фабрикой классов, почему-то
Наверное, это первобытный страх перед рефлекшном.
_FR>
_FR> private static readonly B instance = new B();
_FR> private B() { }
_FR> public static B Instance {
_FR> [DebuggerStepThrough]
_FR> get { return instance; }
_FR> }
_FR> }
_FR>
_FR>ИМХО, это не то дублирование, от которого имеет смысл избавляться.
Зависит от конкретной ситуации.
Я реализую алгоритм на основе конечных автоматов,
синглтоны — это состояния автомата.
Состояний много, они описываются по нескольку в одном классе.
И вот когда видишь пять синглтонов, расположенных подряд, с таким дублированием,
ну оочень хочется это дублирование убрать, ведь это не только лишние строки,
это еще и затрудняет понимание кода.
Здравствуйте, Pro100Oleh, Вы писали:
PO>Здравствуйте, dancingintherain, Вы писали:
D>>необходимо сделать множество классов синглтонов. Чтобы избавиться от повторного кода, хочу вынести функциональность отвечающую за реализацию D>>синглтона в базовый класс.
PO>Неужто так много повторного кода? PO>Я всегда использую следующую конструкцию:
Когда два класса, повторного когда немного,
когда 20, уже начинает напрягать.
Здравствуйте, IB, Вы писали:
IB>Здравствуйте, dancingintherain, Вы писали:
D>>Подскажите, плиз, если кто в курсе, как реализовать такую вещь: D>>необходимо сделать множество классов синглтонов. IB>"Как убить себя гранатой" (с) IB>"Не кладите яйца в микроволновку... "
IB>Сделай отдельно фабрику, которая будет контролировать количество экземпляров нужных классов и создавай объекты через нее. Ну или действительно какой DI контейнер заиспользовать можно, если речь о множестве синглтонов идет. IB>Если нужно, чтобы никто другой до обычных конструкторов не добрался, то можно убрать все в отдельную сборку.
Да, эта идея мне больше всего нравится, и дублирование кода и рефлекшн побеждены и не слишком большой ценой.
Спасибо.
Здравствуйте, dancingintherain, Вы писали:
D>Зависит от конкретной ситуации. D>Я реализую алгоритм на основе конечных автоматов, синглтоны — это состояния автомата. D>Состояний много, они описываются по нескольку в одном классе.
А зачем тут синглтоны?
Что-то мне подсказывает что ты себе на ровном месте создаешь кучу проблем.
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, dancingintherain, Вы писали:
D>Когда два класса, повторного когда немного, D>когда 20, уже начинает напрягать.
Для общего сведения: о вреде синглтонов написано не мало, и на этом форуме в том числе. От них не стоит отказываться полностью, но наличие двух десятков синглтонов однозначно говорит о проблемах в дизайне.