Можно ли таким образом определить абстрактный класс для всех производных синглетонов? - PullRequest
12 голосов
/ 07 ноября 2011

Это мой абстрактный класс, который должен быть получен каждый раз, когда я хочу создать Singleton :

public abstract class Singleton<T> where T : Singleton<T>
{
    private static readonly Lazy<T> _instance = new Lazy<T>(() =>
    {
        var constructor = typeof(T).GetConstructor(BindingFlags.NonPublic |
            BindingFlags.Instance, null, new Type[0], null);

        return (T)constructor.Invoke(null);
    });
    public static T Instance { get { return _instance.Value; } }
    public Singleton() { }
}

Итак, каждый раз, когда мне нужно следовать шаблону проектирования Singleton, я могу просто сделать это:

sealed class Server : Singleton<Server>
{
    private Server() { }
    ...
}

Это совершенно правильно, и если нет, то почему?

Edit:

  • Добавлен приватный конструктор на примере производного класса и вызов на абстрактной базе.

Edit:

  • Переработана инициализация параметра типа.

Ответы [ 5 ]

8 голосов
/ 07 ноября 2011

Нет, вы не можете, потому что когда вы хотите использовать new T(), у вас должен быть открытый конструктор для него, и он отличается от одноэлементного определения. потому что в синглтоне у вас должен быть приватный конструктор, и в этом случае (публичный) каждый может создать новый экземпляр вашего объекта, а это не синглтон.

3 голосов
/ 19 ноября 2011

Реализованные синглтоны - это анти-паттерн.Необходимость наследовать и блокировать ваши классы в определенной форме исчезнет, ​​если вы просто реализуете фабрику:

public class Server {} //Not coupled to any inheritance hierarchy.

public class Factory
{
    private readonly Lazy<Server> _server = new Lazy<Server>(() => new Server());

    public Server Server { get { return _server.Value; } }
}

Однако вы действительно используете фабрику в качестве локатора службы, и локатор службы также считаетсяанти-паттерн, так как вы можете легко использовать DI для внедрения экземпляра сервера в ваши потребляющие классы.

public class MyServerConsumer
{
    public MyServerConsumer(Server server)
    {
      //Do stuff.      
    }
}

Регистрация в стиле Виндзора:

 ... 
 Component.For<Server>();
 ...

Обратите внимание, что слово singleton никогда не бываетупоминается?Вы по-прежнему получаете «единственный экземпляр объекта», но вам не нужно писать код для поддержания этих отношений, и ваши классы не ограничены и не испорчены концепцией «singleton» с самого начала

3 голосов
/ 07 ноября 2011

Этот подход, который вы используете, имеет существенный недостаток. Вы соединяете свои классы с классом Singleton. Помимо того, что вы, как правило, не-SOLID, вы теряете возможность наследовать класс, который вам может понадобиться (если класс не Singleton).

Рекомендуется использовать Dependency Injection и контейнер IoC. Все контейнеры IoC позволяют указать, является ли класс Singleton или нет.

Таким образом, класс полностью игнорирует тот факт, что он создается как Singleton, и это легко изменить на лету, не меняя зависимостей.

1 голос
/ 07 ноября 2011

C # не дает никакой гарантии того, когда будет создано статическое поле _instance. Это связано с тем, что стандарт C # просто утверждает, что классы (которые помечены в IL как BeforeFieldInit) могут инициализировать свои статические поля в любое время до того, как к полю будет произведен доступ. Это означает, что они могут быть инициализированы при первом использовании, они могут быть инициализированы в другое время, вы не можете быть уверены, когда.

Отбросьте Ленивое использование

Я предлагаю отказаться от конструкции Lazy и ввести статический ctor для создания экземпляра. Таким образом, вы используете преимущества BeforeFieldInit стандарта C #, но вы потеряете лень. Хотя это в некотором смысле лениво, оно не создается до того, как используется тип. Это хорошо, так как большинство синглетонов используются в любом случае, когда на них ссылаются.

public abstract class Singleton<T> where T : class, new()
{
    private static readonly T _instance;
    public static T Instance { get { return _instance; } }
    public static Singleton() 
    {
        _instance = new T();
    }
}

Проблема публичного ctor

Теперь у вас проблема в том, что конструкция new T() вынуждает вас иметь публичный ctor. Что не очень хорошо для одиноких. Вы можете использовать закрытый ctor, который вы вызываете через отражение, чтобы решить эту проблему.

0 голосов
/ 25 мая 2014

Это просто комментарий к ответу pjvds (я не могу комментировать обычным способом, потому что у меня недостаточно очков ...).

Вместо использования частного конструктора через отражение, выможет иметь закрытый метод init и вызывать его после «new T ()» в статическом методе Singlton.

...