可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I need a singleton class to be instantiated with some arguments. The way I'm doing it now is:
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...
}
}
The instance is created 'late', meaning I don't have all of the needed arguments on app startup.
In general I don't like forcing an ordering of method calls, but I don't see another way here. The IoC wouldn't resolve it either, since where I can register it in the container, I can also call Create()...
Do you consider this an OK scenario? Do you have some other idea?
edit: I know that what I wrote as an example it's not thread safe, thread-safe isn't part of the question
回答1:
A Singleton with parameters smells fishy to me.
Consider whateva's answer and the following code:
Singleton x = Singleton.getInstance("hello", "world");
Singleton y = Singleton.getInstance("foo", "bar");
Obviously, x==y and y works with x's creation parameters, while y's creation parameters are simply ignored. Results are probably... confusing at least.
If you really, really fell like you have to do it, do it like this:
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);
}
}
In a multithreading environment, add synchronisation to avoid race conditions.
回答2:
Singleton is ugly but since user whateva can't be bothered to correct his own code...
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 about this years ago I think, it's pretty reliable. No exceptions necessary, you aren't in the business of remembering what objects are supposed to be singletons and handling the fallout when you get it wrong.
Edit: the types aren't relevant use what you want, object
is just used here for convenience.
回答3:
Better answer:
Create an interface: ISingleton
(containing whatever actions you want it do to)
And your type: Singleton : ISingleton
Assuming you have access to a UnityContainer:
IUnityContainer _singletonContainer = new UnityContainer(); // or whatever code to initialize the container
- When you are ready to create your type use (assuming you are using Unity for DI):
_singletonContainer.RegisterType(typeof(ISingleton), new Singleton(params));
- If you want to grab the singleton just use:
var localSingletonVar = _singletonContainer.Resolve<ISingleton>();
Note: If the container doesn't have a type registered for the ISingleton interface, then it should either throw exception, either return null.
Old Answer:
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;
}
}
I'd go with something similar (you could need to check if instance was created too), or, if your DI container supports throwing exception on non registered types, i would go with that.
ATTN: Non thread safe code :)
回答4:
The double locking singleton solution provided by annakata will not work everytime on all platforms. there is an flaw in this approach which is well documented. Do not use this approach or you will end up with problems.
The only way to solve this issue is to use the volatile keyword e.g.
private static volatile Singleton m_instance = null;
This is the only thread safe approach.
回答5:
I actually can't see a singleton in your code.
Use a static, parameterized getInstance method which returns the singleton and creates it if it wasn't used before.
回答6:
If you're using .NET 4 (or higher), you can use the System.Lazy type.
It will take care for you the thread safety issue and do it lazy so you won't create instance unnecessarily.
This way the code is short and clean.
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; } }
}
回答7:
/// <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;
}
}