一步一步造个IoC轮子:构造基本的IoC容器
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一步一步造个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
以上是关于一步一步造个IoC轮子:构造基本的IoC容器的主要内容,如果未能解决你的问题,请参考以下文章
.net core发布了,一步一步造个Ioc轮子,弄点.net魔法,近new的速度