我一直在尝试实现单例的方法。我编写了一个小的管理对象,它允许使用更简单、更少的代码方法来编写单例类。
我永远不会在生产系统中使用这样的东西,原因有几个,这让我提出了我的问题。
使用以下代码 - 我假设此实现会导致线程问题和内存泄漏?我说得对吗?
namespace ConsoleApplication1
{
public static class SingletonManager
{
private static readonly Dictionary<string, object> Objects;
static SingletonManager()
{
Objects = new Dictionary<string, object>();
}
public static T InstanceOf<T>(object[] ctorArgs = null)
where T : class
{
var name = typeof (T).FullName;
if (Objects.ContainsKey(name))
return Objects[name] as T;
var ctor = typeof (T).GetConstructors(
BindingFlags.Instance |
BindingFlags.NonPublic)[0];
var instance = ctor.Invoke(ctorArgs) as T;
Objects[name] = instance;
return instance as T;
}
public static void DisposeOf<T>()
where T : Singleton<T>
{
Dispose(typeof (T).FullName);
}
public static void DisposeOf(Type type)
{
Dispose(type.FullName);
}
private static void Dispose(string name)
{
if (!Objects.ContainsKey(name)) return;
var obj = Objects[name];
if (obj is IDisposable)
((IDisposable) Objects[name]).Dispose();
Objects.Remove(name);
}
}
public class Singleton<T>
where T : class
{
private static object ThreadLock = new object();
public static T Instance(object[] ctorArgs = null)
{
lock (ThreadLock)
{
return SingletonManager.InstanceOf<T>(ctorArgs);
}
}
}
public class SomeSingletonClass : Singleton<SomeSingletonClass>
{
public int Number;
private SomeSingletonClass(int i)
{
Number = i;
}
}
internal class Program
{
private static void Main(string[] args)
{
var instance1= SomeSingletonClass.Instance(new object[] {1});
var instance2 = SomeSingletonClass.Instance(new object[] { 2 });
//Is false
var updated = instance1.Number == 2;
instance2.Number = 99;
//Is true
var equals = instance1.Number == instance2.Number;
//Is true
var refEquals = ReferenceEquals(instance1, instance2);
Debugger.Break();
}
}
}
-
经典的单例很少是一个好主意。在大多数情况下,您最好简单地创建一个实例,并将其传递给需要它的代码,而不是强制只有一个实例。IoC 容器将为您完成大部分工作。
-
经典单例的实现非常紧凑,无需进一步简化:
public class MySingleton { private static Lazy<MySingleton> _instance = new Lazy<MySingleton>(() => new MySingleton()); public static MySingleton Instance { get { return _instance.Value; } } private MySingleton() { } }
您最多可以保存其中两行。
-
是的,您的代码不是线程安全的。您需要将大部分内容放在
lock
语句中才能解决此问题。 -
确定构造函数参数的使用者非常可疑。除非它们都相同,否则最终会得到不同的实例,具体取决于哪个使用者首先运行。
这违反了"单一事实来源"原则,是维护和调试的噩梦。
- 您的代码依赖于私有反射。