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

一步一步造个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

时间: 2024-12-26 21:20:57

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

一步一步造个Ioc轮子(二),详解泛型工厂

一步一步造个Ioc轮子目录 .net core发布了,一步一步造个Ioc轮子,弄点.net魔法,近new的速度(一) 一步一步造个Ioc轮子(二),详解泛型工厂 详解泛型工厂 既然我说Ioc容器就是一个豪华版工厂,自动化装配的工厂,那我们就从工厂入手吧,先造个工厂,然后升级成Ioc容器 首先我们来写一个最最最简单的抽象工厂类,还是以前一篇的短信为例 public class SMSFactory { public static ISMS Get() { return new XSMS(); }

.net core发布了,一步一步造个Ioc轮子,弄点.net魔法,近new的速度(一)

前言 .net core正式版前两天发布了,喜大普奔,借此机会,强行来写第一篇博客 第一次写博客,有点紧张,不知怎么才能装做经常写的样子(: 第一次,造个小轮子吧,Ioc容器,借此完善自己的类库 DI,Ioc什么的高大上的名字是什么意思 我不是老司机,开C#时间不太长,以下粗浅个人见解,说错了打脸要轻一点,毕竟是还想靠脸吃饭 (: 怕词不达意,用个示例来说明吧,老司机绕行 小黄是一家电商公司的主程,有一天老板说我们加个发货短信通知发短信给客户吧 于是小黄找了一个叫XSMS的短信通道供应商签约了

Android一步一步实现一款实用的Android广告栏

源码:BannerLayoutDemo 有图有真相: bannerLayoutDemo 开源界有一句很有名的话叫"不要重复发明轮子",当然,我今天的观点不是要反驳这句话,轮子理论给我们的开发带来了极大的便利,项目中要实现一些功能,便去网上找找,一般推荐使用一些有名的库,我本身也是这么做的,但我想说的是,既要会用轮子,也要知道轮子怎么造,必要的时候,自己也要造轮子(想要找到一个完全满意的轮子还是不大容易的). 由来 之前项目里面都是用的daimajia的AndroidImageSlide

(转) 一步一步学习ASP.NET 5 (四)- ASP.NET MVC 6四大特性

转发:微软MVP 卢建晖 的文章,希望对大家有帮助.原文:http://blog.csdn.net/kinfey/article/details/44459625 编者语 : 昨晚写好的文章居然csdn不审核,这个也难怪人,但自己比较忙没办法.分享继续,今天谈ASP.NET MVC 6. 我蛮喜欢Ruby On Rails 这种约定胜于配置的框架,在.NET 有ASP.NET MVC 和Java有Play! Framework .  ASP.NET MVC 版本基本上每年一更新,从不让你失望.我

一步一步学习Vue(十一)

本篇继续学习vuex,还是以实例为主:我们以一步一步学Vue(四)中讲述的例子为基础,对其改造,基于vuex重构一遍,这是原始的代码: todolist.js ; (function () { var list = []; var Todo = (function () { var id = 1; return function (title, desc) { this.title = title; this.desc = desc; this.id = id++; } })(); /** *

.NET跨平台:在Mac上跟着错误信息一步一步手写ASP.NET 5程序

今天坐高铁时尝试了一种学习ASP.NET 5的笨方法,从空文件夹开始,根据运行dnx . kestrel命令的错误信息,一步一步写代码,直至将一个最简单的ASP.NET程序运行起来. 尝试的具体步骤如下. 新建一个空文件夹HelloCnblogs: mkdir HelloCnblogs && cd $_ 在这个空HelloCnblogs文件夹中运行 dnx . kestrel 命令(基于CoreCLR的dnx),运行结果是如下的出错信息: System.InvalidOperationEx

一步一步跟我学习lucene(19)---lucene增量更新和NRT(near-real-time)Query近实时查询

这两天加班,不能兼顾博客的更新,请大家见谅. 有时候我们创建完索引之后,数据源可能有更新的内容,而我们又想像数据库那样能直接体现在查询中,这里就是我们所说的增量索引.对于这样的需求我们怎么来实现呢?lucene内部是没有提供这种增量索引的实现的: 这里我们一般可能会想到,将之前的索引全部删除,然后进行索引的重建.对于这种做法,如果数据源的条数不是特别大的情况下倒还可以,如果数据源的条数特别大的话,势必会造成查询数据耗时,同时索引的构建也是比较耗时的,几相叠加,势必可能造成查询的时候数据缺失的情况

一步一步跟我学习lucene(9)---lucene搜索之拼写检查和相似度查询提示(spellcheck)

suggest应用场景 用户的输入行为是不确定的,而我们在写程序的时候总是想让用户按照指定的内容或指定格式的内容进行搜索,这里就要进行人工干预用户输入的搜索条件了:我们在用百度谷歌等搜索引擎的时候经常会看到按键放下的时候直接会提示用户是否想搜索某些相关的内容,恰好lucene在开发的时候想到了这一点,lucene提供的suggest包正是用来解决上述问题的. suggest包联想词相关介绍 suggest包提供了lucene的自动补全或者拼写检查的支持: 拼写检查相关的类在org.apache.

loadrunner安装运行一步一步来(多图)

安装loadrunner 一路遇到很多坑,很多坑,坑,为什么呢? 因为这软件是收费的,他操作文档写的很详细,就是不写基础环境的配置,下面安装过程写详细一些,减少大家没必要时间上的浪费和对此的谩骂 现在loadrunner 12的版本已经出来了,不过还没有破解,所以先安装测试11的版本,不绕圈子,先下载, 链接: http://pan.baidu.com/s/1kT8CbVh 密码: v4br 加密码是怕被删 遇到的坑 下面是通用的安装说明: 1.下载loadrunner-11.zip文件,解压缩