单例管理实现和线程安全



我一直在尝试实现单例的方法。我编写了一个小的管理对象,它允许使用更简单、更少的代码方法来编写单例类。

永远不会在生产系统中使用这样的东西,原因有几个,这让我提出了我的问题。

使用以下代码 - 我假设此实现会导致线程问题和内存泄漏?我说得对吗?

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();
        }    
    }
}
  1. 经典的单例很少是一个好主意。在大多数情况下,您最好简单地创建一个实例,并将其传递给需要它的代码,而不是强制只有一个实例。IoC 容器将为您完成大部分工作。

  2. 经典单例的实现非常紧凑,无需进一步简化:

    public class MySingleton
    {
        private static Lazy<MySingleton> _instance = new Lazy<MySingleton>(() => new MySingleton());    
        public static MySingleton Instance { get { return _instance.Value; } }
        private MySingleton()
        {
        }
    }
    

    您最多可以保存其中两行。

  3. 是的,您的代码不是线程安全的。您需要将大部分内容放在lock语句中才能解决此问题。

  4. 确定构造函数参数的使用者非常可疑。除非它们都相同,否则最终会得到不同的实例,具体取决于哪个使用者首先运行。

    这违反了"单一事实来源"原则,是维护和调试的噩梦。

  5. 您的代码依赖于私有反射。

最新更新