從Unity到Spring.Net,到Ninject,幾年來陸陸續(xù)續(xù)用過幾個(gè)IoC 框架。雖然會(huì)用,但也沒有一直仔細(xì)的研究過IoC實(shí)現(xiàn)的過程。最近花了點(diǎn)時(shí)間,下了Ninject的源碼,研究了一番,頗有收獲。下面我要實(shí)現(xiàn)一個(gè)最最簡(jiǎn) 單的IoC容器,以讓跟我一樣的小菜能更好的理解IoC框架的到底為我們做了什么。 什么是IoCIoC是英文Inversion of Control的縮寫。我們一般叫它“控制反轉(zhuǎn)”。IoC技術(shù)是用來解決面向?qū)ο笤O(shè)計(jì)一大原則依賴倒置而出現(xiàn)的技術(shù)??梢愿玫膶?shí)現(xiàn)面向接口編程,來使各個(gè)組件之間解耦。 IoC的實(shí)現(xiàn)原理.NET IoC容器的一般就是兩種,一是反射,二是使用Emit來直接寫IL。 廢話不多了,想要了解跟多的IoC的知識(shí)請(qǐng)Google。 關(guān)于實(shí)現(xiàn)先上一張類圖 1.定義IIoCConfig接口public interface IIoCConfig { void AddConfig<TInterface,TType>(); Dictionary<Type, Type> ConfigDictionary { get; } } 2.定義IoCConfig實(shí)現(xiàn)public class IoCConfig:IIoCConfig { /// <summary> /// 存放配置的字典對(duì)象,KEY是接口類型,VALUE是實(shí)現(xiàn)接口的類型 /// </summary> private Dictionary<Type, Type> _configDictionary=new Dictionary<Type, Type>(); /// <summary> /// 添加配置 /// </summary> /// <typeparam name="TInterface">接口</typeparam> /// <typeparam name="TType">實(shí)現(xiàn)接口的類型</typeparam> public void AddConfig<TInterface, TType>() { //判斷TType是否實(shí)現(xiàn)TInterface if (typeof(TInterface).IsAssignableFrom(typeof(TType))) { _configDictionary.Add(typeof(TInterface), typeof(TType)); } else { throw new Exception("類型未實(shí)現(xiàn)接口"); } } public Dictionary<Type, Type> ConfigDictionary { get { return _configDictionary; } } } 使用一個(gè)字典來保存Interface跟Class的對(duì)應(yīng)關(guān)系。這里是仿造Ninject的配置方式,使用代碼來配置。這種配置方式有個(gè)好處就是不 會(huì)寫錯(cuò),因?yàn)橛蠭DE來給你檢查拼寫錯(cuò)誤。不要小看這個(gè)好處,當(dāng)你有上百個(gè)注入對(duì)象的時(shí)候,使用Unity的XML來配置對(duì)應(yīng)關(guān)系的時(shí)候很容易就會(huì)發(fā)生拼 寫錯(cuò)誤。這種錯(cuò)誤往往還很難發(fā)現(xiàn)。 當(dāng)然這里要實(shí)現(xiàn)一個(gè)按照XML配置文件來設(shè)置對(duì)應(yīng)關(guān)系的類也很容易,這里就不實(shí)現(xiàn)了。 3.定義IIoCContainer容器接口public interface IIoCContainer { /// <summary> /// 根據(jù)接口返回對(duì)應(yīng)的實(shí)例 /// </summary> /// <typeparam name="TInterface"></typeparam> /// <returns></returns> TInterface Get<TInterface>(); } 4.使用反射實(shí)現(xiàn)IoC容器public class ReflectionContainer:IIoCContainer { /// <summary> /// 配置實(shí)例 /// </summary> private IIoCConfig _config; /// <summary> /// 構(gòu)造函數(shù) /// </summary> /// <param name="config">ioc配置</param> public ReflectionContainer(IIoCConfig config) { _config = config; } /// <summary> /// 根據(jù)接口獲取實(shí)例對(duì)象 /// </summary> /// <typeparam name="TInterface">接口</typeparam> /// <returns></returns> public TInterface Get<TInterface>() { Type type; var can = _config.ConfigDictionary.TryGetValue(typeof(TInterface), out type); if (can) { //反射實(shí)例化對(duì)象 return (TInterface)Activator.CreateInstance(type); } else { throw new Exception("未找到對(duì)應(yīng)的類型"); } } } 反射這個(gè)代碼太簡(jiǎn)單了,大家都會(huì)用。 5.使用Emit實(shí)現(xiàn)IoC容器public class EmitContainer:IIoCContainer { /// <summary> /// 配置實(shí)例 /// </summary> private IIoCConfig _config; public EmitContainer(IIoCConfig config) { _config = config; } /// <summary> /// 獲取實(shí)例 /// </summary> /// <typeparam name="TInterface">接口</typeparam> /// <returns></returns> public TInterface Get<TInterface>() { Type type; var can = _config.ConfigDictionary.TryGetValue(typeof(TInterface), out type); if (can) { BindingFlags defaultFlags = BindingFlags.Public | BindingFlags.Instance; var constructors = type.GetConstructors(defaultFlags);//獲取默認(rèn)構(gòu)造函數(shù) var t = (TInterface)this.CreateInstanceByEmit(constructors[0]); return t; } else { throw new Exception("未找到對(duì)應(yīng)的類型"); } } /// <summary> /// 實(shí)例化對(duì)象 用EMIT /// </summary> /// <typeparam name="T"></typeparam> /// <param name="constructor"></param> /// <returns></returns> private Object CreateInstanceByEmit(ConstructorInfo constructor) { //動(dòng)態(tài)方法 var dynamicMethod = new DynamicMethod(Guid.NewGuid().ToString("N"), typeof(Object), new[] { typeof(object[]) }, true); //方法IL ILGenerator il = dynamicMethod.GetILGenerator(); //實(shí)例化命令 il.Emit(OpCodes.Newobj, constructor); //如果是值類型裝箱 if (constructor.ReflectedType.IsValueType) il.Emit(OpCodes.Box, constructor.ReflectedType); //返回 il.Emit(OpCodes.Ret); //用FUNC去關(guān)聯(lián)方法 var func = (Func<Object>)dynamicMethod.CreateDelegate(typeof(Func<Object>)); //執(zhí)行方法 return func.Invoke(); } } Emit的實(shí)現(xiàn)是抄自Ninject的實(shí)現(xiàn)方式。這里其實(shí)就是在手動(dòng)書寫IL。一個(gè)簡(jiǎn)單的書寫IL的辦法就是先用C#寫好代碼,然后用Reflector等反編譯工具查看生成的IL,然后改成Emit代碼。 6.實(shí)現(xiàn)IoCContainerManagerpublic class IoCContainerManager { /// <summary> /// 容器 /// </summary> private static IIoCContainer _container; /// <summary> /// 獲取IOC容器 /// </summary> /// <param name="config">ioc配置</param> /// <returns></returns> public static IIoCContainer GetIoCContainer(IIoCConfig config) { if (_container==null) { //反射方式 _container = new ReflectionContainer(config); //EMIT方式 // _container=new EmitContainer(config); } return _container; } } 代碼太簡(jiǎn)單,不多說了。 7.使用public interface ITest { void DoWork(); } public class Test:ITest { public void DoWork() { Console.WriteLine("do work!"); } } class Program { static void Main(string[] args) { IIoCConfig config = new IoCConfig(); config.AddConfig<ITest, Test>();//添加配置 //獲取容器 IIoCContainer container = IoCContainerManager.GetIoCContainer(config); //根據(jù)ITest接口去獲取對(duì)應(yīng)的實(shí)例 ITest test = container.Get<ITest>(); test.DoWork(); Console.Read(); } } 輸出:這里手動(dòng)使用IoC容器去獲取對(duì)應(yīng)的實(shí)例對(duì)象,我們也可以配合特性來使代碼更加簡(jiǎn)單。這里就不實(shí)現(xiàn)了。 8.總結(jié)通過這么短短的幾行代碼。我們實(shí)現(xiàn)了一個(gè)最最簡(jiǎn)單的IoC容器。它可以實(shí)現(xiàn)構(gòu)造函數(shù)注入(默認(rèn)無參)。但是這就已經(jīng)揭示了IoC框架最本質(zhì)的東西: 反射或者EMIT來實(shí)例化對(duì)象。然后我們可以加上緩存,或者一些策略來控制對(duì)象的生命周期,比如是否是單例對(duì)象還是每次都生成一個(gè)新的對(duì)象。 BTW:求 蘇州,上海地區(qū)有激情,有意義的技術(shù)類工作?。?/font>
Email:kklldog@gmail.com
|
|