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

来源:http://www.cnblogs.com/life2009/archive/2016/07/01/ioc3.html
-Advertisement-
Play Games

一步一步造個Ioc輪子目錄 定義容器 首先,我們來畫個大餅,定義好構造函數,註冊函數及獲取函數這幾個最基本的使用方法 接下來我們把上一篇魔改過的泛型工廠再魔改一下,我們把這個工廠去掉static再添加支持泛型委托創建對象的註冊方法,由於整個Ioc設計不是靜態使用的,所以工廠里的內部類static r ...


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

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 現在正在使用WPF開發一個股票K線圖圖表,性能考慮是最大的一方面。 每根柱子寬5像素,柱子和柱子之間的間隔3像素。 一個1920*1080解析度的屏幕,勢必要繪製超過200個柱子。如果選擇的繪製方案不先進,對圖表的繪製是有很大的性能影響的。 任何圖形都由直線和曲線構成的。在WPF中,最底層的圖形的繪 ...
  • 網站管理員希望將別人的整站數據下載到自己的網站里或者將別人網站的一些內容保存到自己的伺服器上。從內容中抽取相關的欄位,發佈到自己的網站系統中。有時需要將網頁相關的文件也保存到本地,如圖片、附件等。 圖片採集軟體能採集任何網站的各種格式圖片,實現把所有文章、新聞、帖子等中間的圖片全部有有序列的分類後保 ...
  • 隨著CYQ.Data 開始回歸免費使用之後,發現用戶的情緒越來越激動,為了保持這持續的激動性,讓我有了開源的念頭。同時,由於框架經過這5-6年來的不斷演進,以前發的早期教程已經太落後了,包括使用方式,及相關介紹,都容易引人誤解。為此,我打算重新寫個系列來介紹最新的版本,讓大伙從傳統的ORM編程過渡到... ...
  • 環境: Ubuntu 16.04 dotnet-dev-1.0.0-preview2-003121 Visual Studio 2015 update 3 Ubuntu 安裝.net core 參考:https://www.microsoft.com/net/core#ubuntu 1.添加源 2. ...
  • 1、安裝.NET Core SDK 在windows下開發.NET Core最好使用Visual Studio工具。下載地址與安裝: VS2015最新版本:Visual Studio 2015 Update 3* VS環境下的.Net Core:.NET Core 1.0 for Visual St... ...
  • 一、前言 上回說到需要做放視頻的使用嚮導,這兩天公司里的老司機一直幫我答疑解惑,讓這個任務變得挺順的,真心感謝他們! 這次與【1】中的不同之處在於: (1)播放和暫停按鈕集成在<MediaElement>的點擊事件之中,點一下是播放,再點一下是暫停 (2)加入了微軟官方改寫的粒子特效 (3)加上了自 ...
  • SQL語句中: 1 2 3 4 1)select * from TableName where StartTime > '2015-04-08' 2)select * from TableName where StartTime >= '2015-04-08' 3)select * from Tab ...
  • 增加: 第一種方法:Response.Cookies[“UserName”].Value=”張三” Response.Cookies[“UserName”].Expires=DateTime.Now.AddDays(1); 第二種方法: HttpCookie cookie=new HttpCooki ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...