代码改变世界

一步一步造个IoC轮子(三):构造基本的IoC容器

2016-07-01 20:47  BlackMagic#  阅读(980)  评论(4编辑  收藏  举报

一步一步造个Ioc轮子目录

一步一步造个IoC轮子(一):Ioc是什么
一步一步造个IoC轮子(二):详解泛型工厂
一步一步造个IoC轮子(三):构造基本的IoC容器

定义容器

首先,我们来画个大饼,定义好构造函数,注册函数及获取函数这几个最基本的使用方法

    /// <summary>
    /// IoC容器
    /// </summary>
    public class Container
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="cfg">配置文件,默认为启动目录下"cfg.xml"</param>
        public Container(string cfg = "cfg.xml")
        {

        }
        /// <summary>
        ///  注册
        /// </summary>
        /// <typeparam name="F">接口或父类</typeparam>
        /// <typeparam name="S">继承类</typeparam>
        /// <param name="name">索引名称,默认为空</param>
        public void Register<F, S>(string name = null) where S : F, new() where F : class
        {

        }
        /// <summary>
        /// 注册单例
        /// </summary>
        /// <typeparam name="F">接口或父类</typeparam>
        /// <typeparam name="S">继承类</typeparam>
        /// <param name="name"></param>
        /// <param name="name">索引名称,默认为空</param>
        public void RegisterSingleton<F, S>(string name = null) where S : F, new() where F : class
        {

        }
        /// <summary>
        /// 注册,对象由传入的Func委托创建
        /// </summary>
        /// <typeparam name="T">接口或父类</typeparam>
        /// <param name="func">对象创建委托</param>
        /// <param name="name">索引名称,默认为空</param>
        public void Register<T>(Func<T> func, string name = null) where T : class
        {

        }
        /// <summary>
        /// 注册单例,对象由传入的Func委托创建
        /// </summary>
        /// <typeparam name="T">接口或父类</typeparam>
        /// <param name="func">对象创建委托</param>
        /// <param name="name">索引名称,默认为空</param>
        public void RegisterSingleton<T>(Func<T> func, string name = null) where T : class
        {

        }
        /// <summary>
        /// 获取
        /// </summary>
        /// <typeparam name="T">接口或父类</typeparam>
        /// <returns>注册的继承类</returns>
        public T Resolve<T>() where T : class
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 获取
        /// </summary>
        /// <typeparam name="T">接口或父类</typeparam>
        /// <param name="name">索引名称</param>
        /// <returns>注册的继承类</returns>
        public T Resolve<T>(string name) where T : class
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 取出当前所有注册的列表
        /// </summary>
        /// <typeparam name="T">接口或父类</typeparam>
        /// <returns>索引名称列表,null表示无索引注册</returns>
        public IList<string> GetRegisterList<T>() where T : class
        {
            throw new NotImplementedException();
        }
    }

接下来我们把上一篇魔改过的泛型工厂再魔改一下,我们把这个工厂去掉static再添加支持泛型委托创建对象的注册方法,由于整个Ioc设计不是静态使用的,所以工厂里的内部类static readonly魔法要退化回双检锁了:(

当然在不使用索引的情况下我们还是可以保留一个魔法单例的_(:з」∠)_

    internal class Factory<T> where T : class
    {
        #region 空间换性能
        private static readonly Factory<T> instance0 = new Factory<T>();
        private static readonly Factory<T> instance1 = new Factory<T>();
        private static readonly ConcurrentDictionary<int, Factory<T>> instances = new ConcurrentDictionary<int, Factory<T>>();
        private static Func<int, Factory<T>> newFunc = (cid) => { return new Factory<T>(); };
        public static Factory<T> GetFactory(int id)
        {
            if (id == 0) return instance0;
            if (id == 1) return instance1;
            return instances.GetOrAdd(id, newFunc);
        }
        #endregion

        #region Creaters
        interface ICreater
        {
            T Create();
        }
        class Creater<U> : ICreater where U : T, new()
        {
            public T Create()
            {
                return new U();
            }
        }
        class FuncCreater : ICreater
        {
            Func<T> func;
            public FuncCreater(Func<T> func)
            {
                this.func = func;
            }
            public T Create()
            {
                return func();
            }
        }
        class MagicSingletonCreater<U> : ICreater where U : T, new()
        {
            class InstanceClass
            {
                public static readonly T Instance = new U();
            }
            public T Create()
            {
                return InstanceClass.Instance;
            }
        }
        class SingletonCreater<U> : ICreater where U : T, new()
        {
            //由于整个IoC容器不是静态的,所以不能用内部类static readonly魔法来搞,否则可能会出现多个索引名称注册了单例子,但引用了同一个对象,多个索引名称变成了别名的情况,只能用双检锁了
            object locker = new object();
            T instance;
            public T Create()
            {
                if (instance == null)
                {
                    lock (locker)
                    {
                        if (instance == null)
                        {
                            Interlocked.Exchange(ref instance, new U());
                        }
                    }
                }
                return instance;
            }
        }
        class FuncSingletonCreater : ICreater
        {
            Func<T> func;
            public FuncSingletonCreater(Func<T> func)
            {
                this.func = func;
            }
            //由于整个IoC容器不是静态的,所以不能用内部类static readonly魔法来搞,否则可能会出现多个索引名称注册了单例子,但引用了同一个对象,多个索引名称变成了别名的情况,只能用双检锁了
            private object locker = new object();
            private T instance;
            public T Create()
            {
                if (instance == null)
                {
                    lock (locker)
                    {
                        if (instance == null)
                        {
                            Interlocked.Exchange(ref instance, func());
                        }
                    }
                }
                return instance;
            }
        }
        class MagicFuncSingletonCreater<S> : ICreater where S : T
        {
            static Func<S> magicFunc;
            public MagicFuncSingletonCreater(Func<S> func)
            {
                magicFunc = func;
            }
            class InstanceClass
            {
                public static readonly S Instance = magicFunc();
            }
            public T Create()
            {
                return InstanceClass.Instance;
            }
        }
        #endregion

        ConcurrentBag<string> regs = new ConcurrentBag<string>();
        public IList<string> GetRegisterList()
        {
            return regs.ToList();
        }
        private void AddReg(string name)
        {
            if (regs.Contains(name)) return;
            regs.Add(name);
        }
        #region 无索引的
        private ICreater creater;
        public T Get()
        {
            return creater.Create();
        }
        public void Reg<S>() where S : T, new()
        {
            creater = new Creater<S>();
            AddReg(null);
        }
        public void RegSingleton<S>() where S : T, new()
        {
            creater = new MagicSingletonCreater<S>();
            AddReg(null);
        }
        public void Reg(Func<T> func)
        {
            creater = new FuncCreater(func);
            AddReg(null);
        }
        public void RegSingleton<S>(Func<S> func) where S : T
        {
            creater = new MagicFuncSingletonCreater<S>(func);
            AddReg(null);
        }
        #endregion

        #region 有索引的
        private IDictionary<string, ICreater> creaters = new ConcurrentDictionary<string, ICreater>();
        public T Get(string key)
        {
            ICreater ct;
            if (creaters.TryGetValue(key, out ct))
                return ct.Create();
            throw new Exception("未注册");
        }
        public void Reg<S>(string key) where S : T, new()
        {
            creaters[key] = new Creater<S>();
            AddReg(key);
        }
        public void RegSingleton<S>(string key) where S : T, new()
        {
            creaters[key] = new SingletonCreater<S>();
            AddReg(key);
        }
        public void Reg(Func<T> func, string key)
        {
            creaters[key] = new FuncCreater(func);
            AddReg(key);
        }
        public void RegSingleton(Func<T> func, string key)
        {
            creaters[key] = new FuncSingletonCreater(func);
            AddReg(key);
        }
        #endregion
    }

好了,有了魔法工厂,IoC容器嘛,不就代理一下这个魔法工厂的操作,来来来,接下来折腾这容器

    /// <summary>
    /// IoC容器
    /// </summary>
    public class Container
    {
        private static volatile int currCid = -1;
        private int cid;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="cfg">配置文件,默认为启动目录下"cfg.xml"</param>
        public Container(string cfg = "cfg.xml")
        {
            cid = Interlocked.Increment(ref currCid);
        }
        /// <summary>
        ///  注册
        /// </summary>
        /// <typeparam name="F">接口或父类</typeparam>
        /// <typeparam name="S">继承类</typeparam>
        /// <param name="name">索引名称,默认为空</param>
        public void Register<F, S>(string name = null) where S : F, new() where F : class
        {
            if (name == null)
                Factory<F>.GetFactory(cid).Reg<S>();
            else
                Factory<F>.GetFactory(cid).Reg<S>(name);
        }
        /// <summary>
        /// 注册单例
        /// </summary>
        /// <typeparam name="F">接口或父类</typeparam>
        /// <typeparam name="S">继承类</typeparam>
        /// <param name="name"></param>
        /// <param name="name">索引名称,默认为空</param>
        public void RegisterSingleton<F, S>(string name = null) where S : F, new() where F : class
        {
            if (name == null)
                Factory<F>.GetFactory(cid).RegSingleton<S>();
            else
                Factory<F>.GetFactory(cid).RegSingleton<S>(name);
        }
        /// <summary>
        /// 注册,对象由传入的Func委托创建
        /// </summary>
        /// <typeparam name="F">接口或父类</typeparam>
        /// <param name="func">对象创建委托</param>
        /// <param name="name">索引名称,默认为空</param>
        public void Register<F>(Func<F> func, string name = null) where F : class
        {
            if (name == null)
                Factory<F>.GetFactory(cid).Reg(func);
            else
                Factory<F>.GetFactory(cid).Reg(func, name);
        }
        /// <summary>
        /// 注册单例,对象由传入的Func委托创建
        /// </summary>
        /// <typeparam name="F">接口或父类</typeparam>
        /// <param name="func">对象创建委托</param>
        /// <param name="name">索引名称,默认为空</param>
        public void RegisterSingleton<F>(Func<F> func, string name = null) where F : class
        {
            if (name == null)
                Factory<F>.GetFactory(cid).RegSingleton(func);
            else
                Factory<F>.GetFactory(cid).RegSingleton(func, name);
        }
        /// <summary>
        /// 获取
        /// </summary>
        /// <typeparam name="F">接口或父类</typeparam>
        /// <returns>注册的继承类</returns>
        public F Resolve<F>() where F : class
        {
            return Factory<F>.GetFactory(cid).Get();
        }
        /// <summary>
        /// 获取
        /// </summary>
        /// <typeparam name="F">接口或父类</typeparam>
        /// <param name="name">索引名称</param>
        /// <returns>注册的继承类</returns>
        public F Resolve<F>(string name) where F : class
        {
            return Factory<F>.GetFactory(cid).Get(name);
        }
        /// <summary>
        /// 取出当前所有注册的列表
        /// </summary>
        /// <typeparam name="F">接口或父类</typeparam>
        /// <returns>索引名称列表,null表示无索引注册</returns>
        public IList<string> GetRegisterList<F>() where F : class
        {
            return Factory<F>.GetFactory(cid).GetRegisterList();
        }
    }

基本的IoC容器已经完成,读取配置的方法我们下一篇再处理,先来点测试吧,看看这个魔法IoC能不能用,性能如何

public static void Main(string[] args)
        {
            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

            var ctx = new Container();

            ctx.RegisterSingleton<ISMS, XSMS>();
            ctx.Register<ISMS, FriendSMS>("fsms");

            var cs = ctx.GetRegisterList<ISMS>();
            foreach (var c in cs)
            {
                //Console.WriteLine("ctx ISMS注册:" + c);
            }
            
            Console.WriteLine("请输入循环次数");
            int max = int.Parse(Console.ReadLine());
            var sw = new Stopwatch();
            sw.Start();
            for (var i = 0; i < max; i++)
            {
                var x = ctx.Resolve<ISMS>();
                x.Send(null, 0, null, null);
            }
            sw.Stop();
            Console.WriteLine("IoC单例耗时{0}ms,平均每次{1}ns", sw.ElapsedMilliseconds, sw.ElapsedMilliseconds * 1000000M / (decimal)max);
            
            var ctx2 = new Container();
            ctx2.Register<ISMS, AlidayuSMS>();
            ctx2.RegisterSingleton<ISMS, XSMS>("fsms");

            sw.Restart();
            for (var i = 0; i < max; i++)
            {
                var x = ctx2.Resolve<ISMS>();
                x.Send(null, 0, null, null);
            }
            sw.Stop();
            Console.WriteLine("IoC创建耗时{0}ms,平均每次{1}ns", sw.ElapsedMilliseconds, sw.ElapsedMilliseconds * 1000000M / (decimal)max);
            sw.Restart();
            for (var i = 0; i < max; i++)
            {
                var x = new XSMS();
                x.Send(null, 0, null, null);
            }
            sw.Stop();
            Console.WriteLine("直接创建耗时{0}ms,平均每次{1}ns", sw.ElapsedMilliseconds, sw.ElapsedMilliseconds * 1000000M / (decimal)max);
            Console.ReadLine();
        }

来看看试试1000万次结果吧

请输入循环次数
10000000
IoC单例耗时181ms,平均每次18.1ns
IoC创建耗时815ms,平均每次81.5ns
直接创建耗时41ms,平均每次4.1ns

VS2015 Release模式下VS直接运行的结果

改用CMD直接运行

几乎一样的速度

改为名称索引的速度如下

比无索引的慢那么一点点,字典的速度最不是盖的,到最后篇我们再看能不能用EMIT织一个类似switch的优化方案,比如参数数量在5以下用if判断,5以上改为更好的普通字典(不是Concurrent那个)

好了这个基本的IoC容器,速度嘛,真泛型魔法加持下,无与伦比,最后一篇优化再出一个静态的版本,速度只会更高:)

 

好了,装逼装到这里该发代码了,注册一只GitHub上传之,Noone