程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 一步一步造個IoC輪子(三):構造基本的IoC容器,ioc輪子

一步一步造個IoC輪子(三):構造基本的IoC容器,ioc輪子

編輯:C#入門知識

一步一步造個IoC輪子(三):構造基本的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

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved