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

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

32 голосов
/ 17 ноября 2010

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

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

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

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

Считаете ли вы это нормальным сценарием? У тебя есть другая идея?

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

Ответы [ 7 ]

32 голосов
/ 17 ноября 2010

Синглтон безобразен, но пользователь 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;
    }
}  

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

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

22 голосов
/ 17 ноября 2010

Синглтон с параметрами пахнет для меня подозрительно.

Рассмотрим ответ 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);             
     } 
}

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

6 голосов
/ 17 ноября 2010

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

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

  2. И ваш тип: Singleton : ISingleton

  3. Если у вас есть доступ к UnityContainer:

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

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

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

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

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

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

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

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-контейнер поддерживает генерирование исключения для не зарегистрированных типов, я бы пошел с этим.

ВНИМАНИЕ: Код без резьбы:)

2 голосов
/ 08 июня 2012

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

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

private static volatile Singleton m_instance = null;

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

1 голос
/ 21 июня 2017

Если вы используете .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; } }
}
0 голосов
/ 05 июля 2018
/// <summary> Generic singleton with double check pattern and with instance parameter </summary>
/// <typeparam name="T"></typeparam>
public class SingleObject<T> where T : class, new()
{
    /// <summary> Lock object </summary>
    private static readonly object _lockingObject = new object();

    /// <summary> Instance </summary>
    private static T _singleObject;

    /// <summary> Protected ctor </summary>
    protected SingleObject()
    {
    }

    /// <summary> Instance with parameter </summary>
    /// <param name="param">Parameters</param>
    /// <returns>Instance</returns>
    public static T Instance(params dynamic[] param)
    {
        if (_singleObject == null)
        {
            lock (_lockingObject)
            {
                if (_singleObject == null)
                {
                    _singleObject = (T)Activator.CreateInstance(typeof(T), param);
                }
            }
        }
        return _singleObject;
    }
}
0 голосов
/ 17 ноября 2010

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

...