it-swarm.com.ru

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

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

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 : я знаю, что то, что я написал в качестве примера, не является поточно-ориентированным, поточно-ориентированным не является частью вопроса

31
veljkoz

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

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

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

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

22
Erich Kitzmueller

Синглтон уродлив, но пользователь 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 просто используется здесь для удобства.

31
annakata

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

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

ВНИМАНИЕ: Не потокобезопасный код :)

6
Bogdan Maxim

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

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

private static volatile Singleton m_instance = null;

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

2
Johnny

Если вы используете .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; } }
}
1
yohait
/// <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
Brick

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

0
DaVinci