c#實現簡單的注入容器,認識注入容器的基本原理
阿新 • • 發佈:2019-01-28
在學習了反射和注入的概念後,加上專案中也用到 比如 AutoFuc 還有 unity 等容器。就有點想寫自己的容器的想法。
然後 搜尋了下 注入容器相關的文章,大多是多某個成熟的注入容器的程式碼進行解析,或者是對 注入概念的解析。成熟的框架考慮到方方面面,原始碼對於新手來說可能不是很好接收(雖然自己也並不是什麼大神),而概念上 哪怕看的多也有可能不知道從何下手。
注入其實主要就靠反射建立物件,只是在建立的時候根據需求給建構函式賦值或者給某個屬性或者欄位賦值。
然後按照自己的理解,寫了一個簡單的容器,使用的是 c#語言,自己寫的這個容器經過在 asp.net mvc 做了注入的測試。
程式碼比較簡單,下面上程式碼:
提供幾個特性(Attribute),用於標識哪個構造或者屬性欄位需要注入:
namespace InjectContainer { [AttributeUsage(AttributeTargets.Constructor)] public class ConstructorInjectAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property)] public class PropertyInjectAttribute : Attribute { } [AttributeUsage(AttributeTargets.Field)] public class FieldInjectAttribute : Attribute { } }
然後是核心程式碼:
namespace InjectContainer { public class Container { private static Dictionary<string, Type> dicToInstances = null; private static Dictionary<string, List<Type>> dicReturnTypeInfo = null; private static object objLock = null; private static Container container = null; static Container() { container = new Container(); dicToInstances = new Dictionary<string, Type>(); dicReturnTypeInfo = new Dictionary<string, List<Type>>(); objLock = new object(); } public Container GetContainer() { return container; } #region 過載註冊器 /// <summary> /// 介面註冊 /// </summary> /// <param name="toNameSpace">目標程式集名稱空間</param> public void Register(string toNameSpace) { var toAssembly = Assembly.Load(toNameSpace); var types = toAssembly.GetTypes(); Register(types); } /// <summary> /// 介面註冊 /// </summary> /// <param name="types">型別陣列</param> public void Register(params Type[] types) { foreach (var type in types) { var interfaces = type.GetInterfaces(); foreach (var inter in interfaces) { if (dicToInstances.ContainsKey(inter.FullName)) continue; dicToInstances.Add(inter.FullName, type); } } } /// <summary> /// 介面註冊 /// </summary> /// <typeparam name="TFrom">來源型別</typeparam> /// <typeparam name="TTo">目標型別</typeparam> public void Register<TFrom, TTo>() { Register(typeof(TFrom), typeof(TTo)); } /// <summary> /// 介面註冊 /// </summary> /// <param name="fromType">來源型別</param> /// <param name="toType">目標型別</param> public void Register(Type fromType, Type toType) { dicToInstances.Add(fromType.FullName, toType); } #endregion /// <summary> /// 獲取實體 /// </summary> /// <typeparam name="T"></typeparam> /// <returns></returns> public T GetInstance<T>() { return GetInstance<T>(typeof(T)); } /// <summary> /// 獲取實體 /// </summary> /// <param name="type"></param> /// <returns></returns> public T GetInstance<T>(Type type) { if (type.IsInterface) { if (dicToInstances.ContainsKey(type.FullName)) return GetInstance<T>(dicToInstances[type.FullName]); else return default(T); } else { return CreateInstance<T>(type); } } private T CreateInstance<T>(Type type) { List<Type> typesOfParameter = new List<Type>(); if (dicReturnTypeInfo.ContainsKey(type.FullName)) { //如果有型別資料就不需要再獲取一次了 typesOfParameter = dicReturnTypeInfo[type.FullName]; } else { lock (objLock) { if (!dicReturnTypeInfo.ContainsKey(type.FullName)) { //建構函式注入 ConstructorInfo constructor = null; var ConstructorsInfo = type.GetConstructors(); if (ConstructorsInfo.Count() > 0) { var dicCountParameters = new Dictionary<int, ParameterInfo[]>(); foreach (var Constructor in ConstructorsInfo) { var tempParameters = Constructor.GetParameters(); dicCountParameters.Add(tempParameters.Count(), tempParameters); if (Constructor.GetCustomAttribute(typeof(ConstructorInjectAttribute)) != null) { constructor = Constructor; break; } } //如果沒有指定特性,則預設取引數最多的一個 var parameters = constructor==null? dicCountParameters.OrderByDescending(c=>c.Key).FirstOrDefault().Value : constructor.GetParameters(); foreach (var item in parameters) { Type fromType = item.ParameterType; typesOfParameter.Add(fromType); } dicReturnTypeInfo.Add(type.FullName, typesOfParameter); } } } } List<object> param = new List<object>(); foreach (var pType in typesOfParameter) { if (dicToInstances.ContainsKey(pType.FullName)) param.Add(GetInstance<object>(dicToInstances[pType.FullName])); else throw new Exception($"指定型別未註冊:{pType.FullName}"); } T t = default(T); if (param.Count > 0) t = (T)Activator.CreateInstance(type, param.ToArray()); else t = (T)Activator.CreateInstance(type); //屬性注入 var properties = type.GetProperties(); foreach (var property in properties) { var attribute = property.GetCustomAttribute(typeof(PropertyInjectAttribute)); if (attribute != null) property.SetValue(t, GetInstance<object>(property.PropertyType)); } //欄位注入 var filds = type.GetFields(); foreach (var fild in filds) { var attribute = fild.GetCustomAttribute(typeof(FieldInjectAttribute)); if (attribute != null) fild.SetValue(t, GetInstance<object>(fild.FieldType)); } return t; } } }
程式碼比較簡單,相對就好理解一些。字典用來儲存註冊了的型別。對外提供主要兩個方法,註冊 Register 和 獲取實體Get Instance,註冊實現介面和實現類的對映關係,獲取實體則生成注入後的型別。
構造注入這裡如果沒有用特性進行標識,則預設取引數最多的一個進行構造。
原始碼已傳到 碼雲