Подтвердить что ты не робот

Могу ли я определить абстрактный класс для всех производных синглтонов таким образом?

Это мой абстрактный класс, который должен быть выведен каждый раз, когда я хочу сделать 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:

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

Ответ 1

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

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; } }
}

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

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

Регистрация стиля Windsor:

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

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

Ответ 2

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

Ответ 3

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

Лучшей практикой является то, что вам нужно использовать инъекцию зависимостей и контейнер IoC. Все контейнеры IoC позволяют указать погоду, что класс является Singleton или нет.

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

Ответ 4

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

Падение Леновое использование

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

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, который вы вызываете через отражение, чтобы решить эту проблему.

Ответ 5

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

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