Синглтон с параметрами
Мне нужен одноэлементный класс, который будет создан с некоторыми аргументами. Теперь я делаю это так:
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: Я знаю, что то, что я написал в качестве примера, не является потокобезопасным, потокобезопасным не является частью вопроса
Ответы
Ответ 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 и создает его, если он раньше не использовался.