你的位置:首页 > ASP.net教程

[ASP.net教程]一步一步造个IoC轮子(三):构造基本的IoC容器


一步一步造个Ioc轮子目录

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

定义容器

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

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