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

Синглтон с параметрами

Мне нужен одноэлементный класс, который будет создан с некоторыми аргументами. Теперь я делаю это так:

class SingletonExample
{
     private SingletonExample mInstance;
     //other members... 
     private SingletonExample()
     {

     } 
     public SingletonExample Instance
     {
         get
         {
              if (mInstance == null)
              {
                  throw new Exception("Object not created");
              }
              return mInstance;
         }
     }

     public void Create(string arg1, string arg2)
     {
         mInstance = new SingletonExample();
         mInstance.Arg1 = arg1;
         mInstance.ObjectCaller = new ObjectCaller(arg2);
         //etc... basically, create object...
     } 
}

Экземпляр создается "late", то есть у меня нет всех необходимых аргументов при запуске приложения.

В общем, мне не нравится заставить упорядочить вызовы методов, но я не вижу другого способа здесь. IoC не разрешит его, так как там, где я могу зарегистрировать его в контейнере, я также могу вызвать Create()...

Считаете ли вы это ОК? Есть ли у вас другая идея?

edit: Я знаю, что то, что я написал в качестве примера, не является потокобезопасным, потокобезопасным не является частью вопроса

4b9b3361

Ответ 1

Синглтон с параметрами пахнет мне.

Рассмотрим ответ whateva и следующий код:

Singleton x = Singleton.getInstance("hello", "world");
Singleton y = Singleton.getInstance("foo", "bar");

Очевидно, что x == y и y работают с параметрами создания x, а параметры создания y просто игнорируются. Результаты, вероятно,... путают по крайней мере.

Если вы действительно, действительно упали, как вы должны это сделать, сделайте это так:

class SingletonExample
{
     private static SingletonExample mInstance;
     //other members... 
     private SingletonExample()
     {  // never used
        throw new Exception("WTF, who called this constructor?!?");
     }
     private SingletonExample(string arg1, string arg2)
     {
         mInstance.Arg1 = arg1;
         mInstance.ObjectCaller = new ObjectCaller(arg2);
         //etc... basically, create object...    
     } 
     public static SingletonExample Instance
     {
         get
         {
              if (mInstance == null)
              {
                  throw new Exception("Object not created");
              }
              return mInstance;
         }
     }

     public static void Create(string arg1, string arg2)
     {
         if (mInstance != null)
         {
             throw new Exception("Object already created");
         }
         mInstance = new SingletonExample(arg1, arg2);             
     } 
}

В многопоточной среде добавьте синхронизацию, чтобы избежать условий гонки.

Ответ 2

Синглтон уродлив, но так как пользователь whateva не может потрудиться, чтобы исправить свой собственный код...

public class Singleton 
{ 
    private static Singleton _instance = null; 

    private static Object _mutex = new Object();

    private Singleton(object arg1, object arg2) 
    { 
        // whatever
    } 

    public static Singleton GetInstance(object arg1, object arg2)
    { 
        if (_instance == null) 
        { 
          lock (_mutex) // now I can claim some form of thread safety...
          {
              if (_instance == null) 
              { 
                  _instance = new Singleton(arg1, arg2);
              }
          } 
        }

        return _instance;
    }
}  

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

Изменить: типы не имеют отношения к использованию, что вам нужно, object используется только для удобства.

Ответ 3

Лучший ответ:

  • Создайте интерфейс: ISingleton (содержащий любые действия, которые вы хотите сделать)

  • И ваш тип: Singleton : ISingleton

  • Предполагая, что у вас есть доступ к UnityContainer:

IUnityContainer _singletonContainer = new UnityContainer(); // or whatever code to initialize the container

  • Когда вы готовы создать свой тип использования (при условии, что вы используете Unity для DI):

_singletonContainer.RegisterType(typeof(ISingleton), new Singleton(params));

  • Если вы хотите захватить синглтон, просто используйте:

var localSingletonVar = _singletonContainer.Resolve<ISingleton>();

Примечание. Если в контейнере нет типа, зарегистрированного для интерфейса ISingleton, тогда он должен либо бросать исключение, либо возвращать null.

Старый ответ:

public class Singleton
{

    private static Singleton instance = null;

    private Singleton(String arg1, String arg2)
    {
    }

    public static Singleton getInstance(String arg1, String arg2)
    {
        if (instance != null)
        {
            throw new InvalidOperationException("Singleton already created - use getinstance()");
        }
        instance = new Singleton(arg1, arg2);
        return instance;
    }

    public static Singleton getInstance()
    {
        if (instance == null)
            throw new InvalidOperationException("Singleton not created - use GetInstance(arg1, arg2)");
        return instance;
    }
}

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

ATTN: безопасный код без потоков:)

Ответ 4

Двойное блокирующее одноточечное решение, предоставляемое annakata, не будет работать каждый раз на всех платформах. есть недостаток в этом подходе, который хорошо документирован. Не используйте этот подход, иначе у вас возникнут проблемы.

Единственный способ решить эту проблему - использовать ключевое слово volatile, например.

private static volatile Singleton m_instance = null;

Это единственный подход, основанный на потоке.

Ответ 5

Если вы используете .NET 4 (или выше), вы можете использовать тип System.Lazy. Он позаботится о вашей проблеме безопасности потока и сделает ее ленивой, поэтому вы не будете создавать экземпляр без необходимости. Таким образом, код является коротким и чистым.

public sealed class Singleton
{
    private static readonly Lazy<Singleton> lazy =
        new Lazy<Singleton>(() => new Singleton(),LazyThreadSafetyMode.ExecutionAndPublication);

    private Singleton()  {  }

    public static Singleton Instance { get { return lazy.Value; } }
}

Ответ 6

На самом деле я не вижу сингла в коде. Используйте статический, параметризованный метод getInstance, который возвращает singleton и создает его, если он раньше не использовался.