讓我手把手教你寫一個強大、方便使用的 IOC 容器
一、介紹
1、介紹
最近無聊,也沒什麼事做,沒事做總是要給自己找點事情做吧,畢竟人的生活在與折騰。於是,決定自己手動寫一個 IOC 的框架。我們知道在 NetCore 的版本裡面已經內置了 IOC 容器,它就是 ServiceCollection,一般情況下,該容器還是夠用的,但是有時候還會有力不從心的時候,比如:我想要實現屬性注入或者方法注入,NetCore 內置的框架就不可以實現。還有情況是,我們要實現對同一介面的多實例注入也沒辦法實現。當然還有其他情況,比如,沒有實現 AOP 的功能。最近正好無事可做,正好利用這段時間,自己親手寫一套 IOC 的框架,當然了,要重寫,肯定要比 NetCore 內置的要強,否則,也就沒有寫的必要了,說干就干。
2、開發環境
1)、作業系統:Windows 10 專業版本。
2)、開發工具:Visual Studio 2019 社區版,16.8.3
3)、開發語言:C#
4)、框架版本:Net 5.0,該版本是跨平台版本,但是不是長期版本,6.0 是 LTS 版本。
3、實現目標
1)、該框架可以實現構造函數注入。
2)、該框架可以實現方法注入。
3)、該框架可以實現屬性注入。
4)、該框架可以實現無限層級激活。
5)、該框架可以實現註冊服務的多種聲明周期,分別是:Transient,Singleton,Scoped,PerThread
6)、該框架可以實現針對同一介面的多實例註冊。
7)、當一個類型在實例化的時候可以增加參數。
以上就是該框架的目標,應該還不錯吧。畢竟我們自己手寫了框架程式碼,讓我們會更加了解 IOC 的定義和實現。
二、手寫框架
1、我先把該框架的主要類型的程式碼貼出來,這個類型是核心類型,實現了我們上面定義的目標。
首先、我們為 IOC 容器定義介面,面向介面編程嘛,可不要忘記了,所以,我們先頂一個介面,類型名稱:ICustomContainer。
1 /// <summary> 2 /// 我們定義的 IOC 容器抽象基類型,它定義了 IOC 容器的核心功能。 3 /// </summary> 4 public interface ICustomContainer 5 { 6 /// <summary> 7 /// 提供服務的名稱和參數構建實例以 TFrom 類型註冊 TTo 實例。 8 /// </summary> 9 /// <typeparam name="TFrom">TTo 的基類類型。</typeparam> 10 /// <typeparam name="TTo">TFrom 的子類類型。</typeparam> 11 /// <param name="serviceName">要註冊服務的名稱。</param> 12 /// <param name="lifetime">要註冊的服務的生命周期。</param> 13 /// <param name="parameterValues">要註冊的服務在構建實例時需要的非注入參數。</param> 14 void Register<TFrom, TTo>(string serviceName, ServiceLifetime lifetime, params object[] parameterValues) where TTo : TFrom; 15 16 /// <summary> 17 /// 以指定名稱解析該基類型的實例。 18 /// </summary> 19 /// <typeparam name="TFrom">要解析實例的基類型。</typeparam> 20 /// <param name="serviceName">要解析實例的名稱。</param> 21 /// <returns></returns> 22 TFrom Resolve<TFrom>(string serviceName); 23 }
再者,就是該介面的實現類型,該類型也是該容器的核心程式碼。程式碼不是很難,大家直接看吧。
類型名稱:PatrickContainer
1 using System; 2 using System.Collections.Concurrent; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Reflection; 6 using System.Threading; 7 8 namespace PatrickLiu.NetCore50.IOCFramework.Container 9 { 10 /// <summary> 11 /// 自定義的IOC容器實現。 12 /// </summary> 13 public sealed class PatrickContainer: ICustomContainer 14 { 15 private readonly IDictionary<string, ServiceMetadata> _Container; 16 private readonly IDictionary<string, object[]> _Parameters; 17 private readonly IDictionary<string, object> _ScopedContainer; 18 19 20 /// <summary> 21 /// 初始化類型的新實例,實例化容器。 22 /// </summary> 23 public PatrickContainer() 24 { 25 _Container = new ConcurrentDictionary<string, ServiceMetadata>(); 26 _Parameters = new ConcurrentDictionary<string, object[]>(); 27 _ScopedContainer = new Dictionary<string, object>(); 28 } 29 30 /// <summary> 31 /// 可以創建子作用域。 32 /// </summary> 33 /// <returns></returns> 34 public PatrickContainer CreateScoped() 35 { 36 return new PatrickContainer(_Container, _Parameters, new Dictionary<string, object>()); 37 } 38 39 /// <summary> 40 /// 通過是有構造函數初始化容器。 41 /// </summary> 42 /// <param name="container"></param> 43 /// <param name="parameters"></param> 44 /// <param name="scopedContainer"></param> 45 private PatrickContainer(IDictionary<string, ServiceMetadata> container, IDictionary<string, object[]> parameters, IDictionary<string, object> scopedContainer) 46 { 47 this._Container = container; 48 this._Parameters = parameters; 49 this._ScopedContainer = scopedContainer; 50 } 51 52 /// <summary> 53 /// 以 TFrom 類型註冊 TTo 實例。 54 /// </summary> 55 /// <typeparam name="TFrom">TTo 的基類類型</typeparam> 56 /// <typeparam name="TTo">TFrom 的子類類型。</typeparam> 57 public void Register<TFrom, TTo>() where TTo : TFrom 58 { 59 Register<TFrom, TTo>(null, ServiceLifetime.Transient, null); 60 } 61 62 /// <summary> 63 /// 以 TFrom 類型註冊 TTo 實例。 64 /// </summary> 65 /// <typeparam name="TFrom">TTo 的基類類型</typeparam> 66 /// <typeparam name="TTo">TFrom 的子類類型。</typeparam> 67 /// <param name="lifetime">要註冊的服務的生命周期。</param> 68 public void Register<TFrom, TTo>(ServiceLifetime lifetime) where TTo : TFrom 69 { 70 Register<TFrom, TTo>(null, lifetime, null); 71 } 72 73 /// <summary> 74 /// 提供服務構建實例所需參數來以 TFrom 類型註冊 TTo 實例。 75 /// </summary> 76 /// <typeparam name="TFrom">TTo 的基類類型</typeparam> 77 /// <typeparam name="TTo">TFrom 的子類類型。</typeparam> 78 /// <param name="parameterValues">要註冊的服務在構建實例時需要的非注入參數。</param> 79 public void Register<TFrom, TTo>(params object[] parameterValues) where TTo : TFrom 80 { 81 Register<TFrom, TTo>(null, ServiceLifetime.Transient, parameterValues); 82 } 83 84 /// <summary> 85 /// 提供服務的名稱構建實例來以 TFrom 類型註冊 TTo 實例。 86 /// </summary> 87 /// <typeparam name="TFrom">TTo 的基類類型</typeparam> 88 /// <typeparam name="TTo">TFrom 的子類類型。</typeparam> 89 /// <param name="serviceName">要註冊服務的名稱。</param> 90 public void Register<TFrom, TTo>(string serviceName) where TTo : TFrom 91 { 92 Register<TFrom, TTo>(serviceName, ServiceLifetime.Transient, null); 93 } 94 95 /// <summary> 96 /// 提供服務的名稱構建實例來以 TFrom 類型註冊 TTo 實例。 97 /// </summary> 98 /// <typeparam name="TFrom">TTo 的基類類型</typeparam> 99 /// <typeparam name="TTo">TFrom 的子類類型。</typeparam> 100 /// <param name="serviceName">要註冊服務的名稱。</param> 101 /// <param name="lifetime">要註冊的服務的生命周期。</param> 102 public void Register<TFrom, TTo>(string serviceName, ServiceLifetime lifetime) where TTo : TFrom 103 { 104 Register<TFrom, TTo>(serviceName, lifetime, null); 105 } 106 107 /// <summary> 108 /// 提供服務的名稱和參數構建實例以 TFrom 類型註冊 TTo 實例。 109 /// </summary> 110 /// <typeparam name="TFrom">TTo 的基類類型</typeparam> 111 /// <typeparam name="TTo">TFrom 的子類類型。</typeparam> 112 /// <param name="serviceName">要註冊服務的名稱。</param> 113 /// <param name="lifetime">要註冊的服務的生命周期。</param> 114 /// <param name="parameterValues">要註冊的服務在構建實例時需要的非注入參數。</param> 115 public void Register<TFrom, TTo>(string serviceName, ServiceLifetime lifetime, params object[] parameterValues) where TTo : TFrom 116 { 117 string key; 118 if (string.IsNullOrEmpty(serviceName) || string.IsNullOrWhiteSpace(serviceName)) 119 { 120 key = typeof(TFrom).FullName; 121 if (!_Container.ContainsKey(key)) 122 { 123 _Container.Add(key, new ServiceMetadata() { ServiceType = typeof(TTo), Lifetime = lifetime }); 124 } 125 } 126 else 127 { 128 key = string.Format("{0}_{1}", typeof(TFrom).FullName, serviceName); 129 if (!_Container.ContainsKey(key)) 130 { 131 _Container.Add(key, new ServiceMetadata() { ServiceType = typeof(TTo), Lifetime = lifetime }); 132 } 133 } 134 if (parameterValues != null && parameterValues.Length > 0) 135 { 136 _Parameters.Add(key, parameterValues); 137 } 138 } 139 140 /// <summary> 141 /// 以指定類型解析該類型的實例。 142 /// </summary> 143 /// <typeparam name="TFrom">要解析實例的基類型。</typeparam> 144 /// <returns></returns> 145 public TFrom Resolve<TFrom>() 146 { 147 return Resolve<TFrom>(null); 148 } 149 150 /// <summary> 151 /// 以指定名稱解析該基類型的實例。 152 /// </summary> 153 /// <typeparam name="TFrom">要解析實例的基類型。</typeparam> 154 /// <param name="serviceName">要解析實例的名稱。</param> 155 /// <returns></returns> 156 public TFrom Resolve<TFrom>(string serviceName) 157 { 158 return (TFrom)Create(typeof(TFrom), serviceName); 159 } 160 161 162 /// <summary> 163 /// 通過遞歸實現解析實例對象。 164 /// </summary> 165 /// <param name="baseType">服務的基類型。</param> 166 /// <param name="serviceName">服務實例的名稱。</param> 167 /// <returns></returns> 168 private object Create(Type baseType, string serviceName = null) 169 { 170 #region 處理關鍵字 171 172 string keyword; 173 174 if (string.IsNullOrEmpty(serviceName) || string.IsNullOrWhiteSpace(serviceName)) 175 { 176 keyword = string.Format("{0}", baseType.FullName); 177 } 178 else 179 { 180 keyword = string.Format("{0}_{1}", baseType.FullName, serviceName); 181 } 182 183 #endregion 184 185 Type targetType = null; ServiceLifetime lifetime = ServiceLifetime.Transient; 186 if (_Container.ContainsKey(keyword)) 187 { 188 targetType = _Container[keyword].ServiceType; 189 lifetime = _Container[keyword].Lifetime; 190 } 191 else if (keyword.IndexOf('_') != -1) 192 { 193 if (_Container.ContainsKey(keyword.Split('_')[0])) 194 { 195 keyword = keyword.Split('_')[0]; 196 targetType = _Container[keyword].ServiceType; 197 lifetime = _Container[keyword].Lifetime; 198 } 199 } 200 else 201 { 202 throw new Exception("類型還未註冊!"); 203 } 204 205 #region 生命周期 206 207 switch (lifetime) 208 { 209 case ServiceLifetime.Transient: 210 break; 211 case ServiceLifetime.Singleton: 212 if (_Container[keyword].SingletonInstance != null) 213 { 214 return _Container[keyword].SingletonInstance; 215 } 216 break; 217 case ServiceLifetime.Scoped: 218 if (_ScopedContainer.ContainsKey(keyword)) 219 { 220 return _ScopedContainer[keyword]; 221 } 222 break; 223 case ServiceLifetime.PerThread: 224 var objInstance = CallContext.GetData($"{keyword}{Thread.CurrentThread.ManagedThreadId}"); 225 if (objInstance != null) 226 { 227 return objInstance; 228 } 229 break; 230 default: 231 break; 232 } 233 234 #endregion 235 236 #region 選擇構造函數 237 238 ConstructorInfo ctor = null; 239 240 //1、通過特性約束 241 ctor = targetType.GetConstructors().FirstOrDefault(c => c.IsDefined(typeof(SelectedConstructorAttribute), true)); 242 243 if (ctor == null) 244 { 245 //2、參數最多的 246 ctor = targetType.GetConstructors().OrderByDescending(c => c.GetParameters().Length).First(); 247 } 248 249 #endregion 250 251 #region 核心創建對象程式碼 252 253 IList<object> parameters = new List<object>(); 254 var values = _Parameters.ContainsKey(keyword) ? _Parameters[keyword] : null; 255 int index = 0; 256 foreach (var parameter in ctor.GetParameters()) 257 { 258 if (values != null && values.Length > 0 && parameter.IsDefined(typeof(ConstantPatameterAttribute), true)) 259 { 260 parameters.Add(values[index++]); 261 } 262 else 263 { 264 var parameterType = parameter.ParameterType; 265 var instance = Create(parameterType, serviceName); 266 parameters.Add(instance); 267 } 268 } 269 object oIntance = Activator.CreateInstance(targetType, parameters.ToArray()); 270 271 #endregion 272 273 #region 屬性注入 274 275 Type propertyType = null; 276 foreach (var property in targetType.GetProperties().Where(p => p.IsDefined(typeof(InjectionPropertyAttribute), true))) 277 { 278 propertyType = property.PropertyType; 279 var propInstance = Create(propertyType); 280 property.SetValue(oIntance, propInstance); 281 } 282 283 #endregion 284 285 #region 方法注入 286 287 foreach (var methodInfo in targetType.GetMethods().Where(p => p.IsDefined(typeof(InjectionMethodAttribute), true))) 288 { 289 IList<object> methodParameters = new List<object>(); 290 values = _Parameters.ContainsKey(keyword) ? _Parameters[keyword] : null; 291 index = 0; 292 foreach (var parameter in methodInfo.GetParameters()) 293 { 294 if (values != null && values.Length > 0 && parameter.IsDefined(typeof(ConstantPatameterAttribute))) 295 { 296 methodParameters.Add(values[index++]); 297 } 298 else 299 { 300 var methodParaType = parameter.ParameterType; 301 var paraInstance = Create(methodParaType, serviceName); 302 methodParameters.Add(paraInstance); 303 } 304 } 305 methodInfo.Invoke(oIntance, methodParameters.ToArray()); 306 } 307 308 #endregion 309 310 #region 生命周期 311 312 switch (lifetime) 313 { 314 case ServiceLifetime.Transient: 315 break; 316 case ServiceLifetime.Singleton: 317 if (_Container[keyword].SingletonInstance == null) 318 { 319 _Container[keyword].SingletonInstance = oIntance; 320 } 321 break; 322 case ServiceLifetime.Scoped: 323 if (!_ScopedContainer.ContainsKey(keyword)) 324 { 325 _ScopedContainer.Add(keyword, oIntance); 326 } 327 break; 328 case ServiceLifetime.PerThread: 329 CallContext.SetData($"{keyword}{Thread.CurrentThread.ManagedThreadId}", oIntance); 330 break; 331 default: 332 break; 333 } 334 335 #endregion 336 337 return oIntance; 338 } 339 } 340 }
當然了,還有一些其他的輔助類型,這麼大的框架,還是要需要一些輔助類型的,接下來我們一一介紹。
2、ConstantPatameterAttribute類型
該類型是一個標記特性,用於標註不需要注入而進行傳遞的參數,可以使用該屬性。
1 using System; 2 3 namespace PatrickLiu.NetCore50.IOCFramework.Container 4 { 5 /// <summary> 6 /// 該類型定義了在服務初始化的時候需要從外界出入的參數,如果參數被標註,則說明改參數所需要的參數從外界傳入。該類型是密封類型,不可以被繼承。 7 /// </summary> 8 [AttributeUsage(AttributeTargets.Parameter|AttributeTargets.GenericParameter,AllowMultiple =false,Inherited =true)] 9 public sealed class ConstantPatameterAttribute:Attribute 10 { 11 } 12 }
3、InjectionMethodAttribute 類型
該類型也是一個標記特性,用於標記方法,可以通過方法實現注入。
1 using System; 2 3 namespace PatrickLiu.NetCore50.IOCFramework.Container 4 { 5 /// <summary> 6 /// 該類型定義了方法注入的特性類型,該類型是密封類型,不可以被繼承。它也是一個標識類型。 7 /// </summary> 8 [AttributeUsage(AttributeTargets.Method,AllowMultiple =false,Inherited =true)] 9 public sealed class InjectionMethodAttribute:Attribute 10 { 11 } 12 }
4、InjectionPropertyAttribute 類型。
該類型也是一個標記特性,用於標記正在屬性上,可以通過屬性實現注入。
1 using System; 2 3 namespace PatrickLiu.NetCore50.IOCFramework.Container 4 { 5 /// <summary> 6 /// 該特性是一個實現屬性注入的特性類,該類型是密封的。它是一個標識屬性。 7 /// </summary> 8 [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] 9 public sealed class InjectionPropertyAttribute : Attribute 10 { 11 } 12 }
5、SelectedConstructorAttribute 類型
在我們構建類型實例的時候,可以通過該特性選擇通過哪個構造函數創建實例。默認情況是選擇參數最多的構造函數,也可以通過該特性選擇構造函數。
1 using System; 2 3 namespace PatrickLiu.NetCore50.IOCFramework.Container 4 { 5 /// <summary> 6 /// 選擇構造函數。 7 /// </summary> 8 [AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = true)] 9 public sealed class SelectedConstructorAttribute : Attribute 10 { 11 } 12 }
6、ServiceLifetime 枚舉類型。
我們可以實現對註冊服務的生命周期的管理,類型簡單,不多說了。
1 namespace PatrickLiu.NetCore50.IOCFramework.Container 2 { 3 /// <summary> 4 /// 服務的生命周期。 5 /// </summary> 6 public enum ServiceLifetime 7 { 8 /// <summary> 9 /// 瞬時服務實例。 10 /// </summary> 11 Transient, 12 13 /// <summary> 14 /// 單例服務實例。 15 /// </summary> 16 Singleton, 17 18 /// <summary> 19 /// 作用域服務實例。 20 /// </summary> 21 Scoped, 22 23 /// <summary> 24 /// 執行緒服務實例。 25 /// </summary> 26 PerThread 27 } 28 }
7、ServiceMetadata 類型。
用於定義註冊服務的對象。
1 using System; 2 3 namespace PatrickLiu.NetCore50.IOCFramework.Container 4 { 5 /// <summary> 6 /// 該類型定義了註冊服務的元數據。 7 /// </summary> 8 public sealed class ServiceMetadata 9 { 10 /// <summary> 11 /// 獲取或者設置註冊服務的類型。 12 /// </summary> 13 public Type ServiceType { get; set; } 14 15 /// <summary> 16 /// 獲取或者設置註冊服務的生命周期。 17 /// </summary> 18 public ServiceLifetime Lifetime { get; set; } 19 20 /// <summary> 21 /// 獲取或者設置單件的服務實例。 22 /// </summary> 23 public Object SingletonInstance { get; set; } 24 } 25 }
8、CallContext類型。
在NetCore 環境中,沒有CallContext 類型,所以只能自己實現一個,可以實現基於執行緒來管理註冊服務的生命周期。
1 using System.Collections.Concurrent; 2 using System.Threading; 3 4 namespace PatrickLiu.NetCore50.IOCFramework.Container 5 { 6 /// <summary> 7 /// Provides a way to set contextual data that flows with the call and async context of a test or invocation. 8 /// </summary> 9 public static class CallContext 10 { 11 private static ConcurrentDictionary<string, AsyncLocal<object>> state = new ConcurrentDictionary<string, AsyncLocal<object>>(); 12 13 /// <summary> 14 /// Stores a given object and associates it with the specified name. 15 /// </summary> 16 /// <param name="name">The name with which to associate the new item in the call context.</param> 17 /// <param name="data">The object to store in the call context.</param> 18 public static void SetData(string name, object data) => 19 state.GetOrAdd(name, _ => new AsyncLocal<object>()).Value = data; 20 21 /// <summary> 22 /// Retrieves an object with the specified name from the <see cref="CallContext"/>. 23 /// </summary> 24 /// <param name="name">The name of the item in the call context.</param> 25 /// <returns>The object in the call context associated with the specified name, or <see langword="null"/> if not found.</returns> 26 public static object GetData(string name) => 27 state.TryGetValue(name, out AsyncLocal<object> data) ? data.Value : null; 28 } 29 30 /// <summary> 31 /// 可以測試 32 /// </summary> 33 /// <typeparam name="T"></typeparam> 34 public static class CallContext<T> 35 { 36 static ConcurrentDictionary<string, AsyncLocal<T>> state = new ConcurrentDictionary<string, AsyncLocal<T>>(); 37 38 /// <summary> 39 /// Stores a given object and associates it with the specified name. 40 /// </summary> 41 /// <param name="name">The name with which to associate the new item in the call context.</param> 42 /// <param name="data">The object to store in the call context.</param> 43 public static void SetData(string name, T data) => 44 state.GetOrAdd(name, _ => new AsyncLocal<T>()).Value = data; 45 46 /// <summary> 47 /// Retrieves an object with the specified name from the <see cref="CallContext"/>. 48 /// </summary> 49 /// <typeparam name="T">The type of the data being retrieved. Must match the type used when the <paramref name="name"/> was set via <see cref="SetData{T}(string, T)"/>.</typeparam> 50 /// <param name="name">The name of the item in the call context.</param> 51 /// <returns>The object in the call context associated with the specified name, or a default value for <typeparamref name="T"/> if none is found.</returns> 52 public static T GetData(string name) => 53 state.TryGetValue(name, out AsyncLocal<T> data) ? data.Value : default(T); 54 } 55 }
三、測試程式碼
我們為了更好的測試我們寫的IOC容器,我另外建立兩個獨立的類庫項目和一個控制台應用程式。當然了,引用關係別忘記了。
1、我們定義的介面類庫,裡面包含了測試用到的所有介面類型。很簡單,不多說。
以下就是我們介面的程式碼了。
1 /// <summary> 2 /// ServiceA的服務介面 3 /// </summary> 4 public interface IServiceA 5 { 6 void Show(); 7 }
1 public interface IServiceB 2 { 3 void Show(); 4 }
1 public interface IServiceC 2 { 3 void Show(); 4 }
1 public interface IServiceD 2 { 3 void Show(); 4 }
1 public interface IServiceE 2 { 3 void Show(); 4 }
1 public interface IServiceF 2 { 3 void Show(); 4 }
2、第一步我們定義了介面類庫,這裡我們定義實現了介面類庫的服務類庫,很簡單,不多說。
以下是實現程式碼,很簡單,不多說。
1 /// <summary> 2 /// 自定義類型的服務。 3 /// </summary> 4 public class MyServiceA : IServiceA 5 { 6 public MyServiceA() 7 { 8 Console.WriteLine("MyServiceA is Created"); 9 } 10 11 /// <summary> 12 /// 方法執行 13 /// </summary> 14 public void Show() 15 { 16 Console.WriteLine("MyServiceA-show()"); 17 } 18 }
1 /// <summary> 2 /// 自定義類型服務第二版本。 3 /// </summary> 4 public sealed class MyServiceA2 : IServiceA 5 { 6 private IServiceF _IServiceF; 7 8 /// <summary> 9 /// 構造函數 10 /// </summary> 11 public MyServiceA2() 12 { 13 Console.WriteLine("MyServiceA2 is created"); 14 } 15 16 /// <summary> 17 /// 方法注入。 18 /// </summary> 19 /// <param name="service"></param> 20 [InjectionMethod] 21 public void MethodInjection(IServiceF service) 22 { 23 _IServiceF = service; 24 } 25 26 /// <summary> 27 /// 方法執行。 28 /// </summary> 29 public void Show() 30 { 31 Console.WriteLine("MyServiceA2--Show()"); 32 } 33 }
1 public class MyServiceA3 : IServiceA 2 { 3 /// <summary> 4 /// 5 /// </summary> 6 /// <param name="age"></param> 7 /// <param name="name"></param> 8 /// <param name="school"></param> 9 public MyServiceA3([ConstantPatameter]int age, [ConstantPatameter] string name, [ConstantPatameter] string school) 10 { 11 Console.WriteLine($"{age}{name}{school} MyServiceA3 is created"); 12 } 13 14 15 public void Show() 16 { 17 Console.WriteLine("MyServiceA3-Show() is executed!"); 18 } 19 }
1 public class MyServiceA4 : IServiceA 2 { 3 private IServiceF _IServiceF; 4 5 public MyServiceA4() 6 { 7 Console.WriteLine(""); 8 } 9 10 /// <summary> 11 /// 方法注入 12 /// </summary> 13 /// <param name="service">注入服務</param> 14 /// <param name="age"></param> 15 /// <param name="name"></param> 16 [InjectionMethod] 17 public void MethodInjection(IServiceF service,[ConstantPatameter]int age,[ConstantPatameter]string name) 18 { 19 _IServiceF = service; 20 Console.WriteLine($"{name} 今年 {age} 歲了。"); 21 } 22 23 public void Show() 24 { 25 Console.WriteLine("MyServiceA4--show() executing"); 26 } 27 }
1 /// <summary> 2 /// 3 /// </summary> 4 public class MyServiceB : IServiceB 5 { 6 /// <summary> 7 /// 8 /// </summary> 9 /// <param name="serviceC"></param> 10 /// <param name="serviceE"></param> 11 public MyServiceB(IServiceC serviceC,IServiceE serviceE) 12 { 13 Console.WriteLine("MyServiceB is created"); 14 } 15 16 /// <summary> 17 /// 18 /// </summary> 19 public void Show() 20 { 21 Console.WriteLine("MyServiceB-Show()"); 22 } 23 }
1 /// <summary> 2 /// 3 /// </summary> 4 public class MyServiceC : IServiceC 5 { 6 /// <summary> 7 /// 8 /// </summary> 9 /// <param name="serviceD"></param> 10 public MyServiceC(IServiceD serviceD) 11 { 12 Console.WriteLine("MyServiceC is created"); 13 } 14 15 /// <summary> 16 /// 17 /// </summary> 18 public void Show() 19 { 20 Console.WriteLine("MyServiceC-Show()"); 21 } 22 }
1 /// <summary> 2 /// 自定義類型的服務。 3 /// </summary> 4 public class MyServiceD : IServiceD 5 { 6 /// <summary> 7 /// 構造函數 8 /// </summary> 9 public MyServiceD() 10 { 11 Console.WriteLine("MyServiceD is created"); 12 } 13 14 /// <summary> 15 /// 方法執行。 16 /// </summary> 17 public void Show() 18 { 19 Console.WriteLine("MyServiceD--show()"); 20 } 21 }
1 /// <summary> 2 /// 自定義服務類型。 3 /// </summary> 4 public class MyServiceE : IServiceE 5 { 6 /// <summary> 7 /// 構造函數 8 /// </summary> 9 public MyServiceE() 10 { 11 Console.WriteLine("MyServiceE is created"); 12 } 13 14 /// <summary> 15 /// 屬性注入 16 /// </summary> 17 [InjectionProperty] 18 public IServiceF ServiceF { get; set; } 19 20 /// <summary> 21 /// 方法執行。 22 /// </summary> 23 public void Show() 24 { 25 Console.WriteLine("MyServiceE-Show()"); 26 } 27 }
1 /// <summary> 2 /// 自定義類型服務。 3 /// </summary> 4 public sealed class MyServiceF : IServiceF 5 { 6 /// <summary> 7 /// 構造函數 8 /// </summary> 9 public MyServiceF() 10 { 11 Console.WriteLine("MyServiceF is created!"); 12 } 13 14 /// <summary> 15 /// 方法執行。 16 /// </summary> 17 public void Show() 18 { 19 Console.WriteLine("MyServiceF--Show()"); 20 } 21 }
3、這個程式碼就是我們控制台項目,用來做測試的,很簡單,不多說了。
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 //最簡單版本 6 { 7 PatrickContainer container = new PatrickContainer(); 8 container.Register<IServiceA, MyServiceA>(); 9 10 var instance = container.Resolve<IServiceA>(); 11 instance.Show(); 12 } 13 //可以多層依賴,可以包含屬性注入 14 { 15 PatrickContainer container = new PatrickContainer(); 16 container.Register<IServiceA, MyServiceA>(); 17 container.Register<IServiceB, MyServiceB>(); 18 container.Register<IServiceC, MyServiceC>(); 19 container.Register<IServiceD, MyServiceD>(); 20 container.Register<IServiceE, MyServiceE>(); 21 container.Register<IServiceF, MyServiceF>(); 22 23 var instance = container.Resolve<IServiceB>(); 24 instance.Show(); 25 } 26 //單介面多實例,也包含方法注入 27 { 28 PatrickContainer container = new PatrickContainer(); 29 container.Register<IServiceA, MyServiceA>("A"); 30 container.Register<IServiceA, MyServiceA2>("A2"); 31 container.Register<IServiceF, MyServiceF>(); 32 33 var instance = container.Resolve<IServiceA>("A"); 34 instance.Show(); 35 instance = container.Resolve<IServiceA>("A2"); 36 instance.Show(); 37 } 38 //構造函數參數、方法參數 39 { 40 PatrickContainer container = new PatrickContainer(); 41 42 container.Register<IServiceA, MyServiceA3>(20,"zhangfei","淶源第一中學"); 43 44 var instance = container.Resolve<IServiceA>(); 45 instance.Show(); 46 } 47 { 48 PatrickContainer container = new PatrickContainer(); 49 50 container.Register<IServiceA, MyServiceA4>(20, "張飛"); 51 container.Register<IServiceF, MyServiceF>(); 52 53 var instance = container.Resolve<IServiceA>(); 54 instance.Show(); 55 } 56 //聲明周期 57 { 58 //單例 59 PatrickContainer container = new PatrickContainer(); 60 container.Register<IServiceA, MyServiceA>(ServiceLifetime.Singleton); 61 62 63 var instance = container.Resolve<IServiceA>(); 64 var instance2 = container.Resolve<IServiceA>(); 65 66 Console.WriteLine(Object.ReferenceEquals(instance,instance2));//True 67 } 68 // 69 { 70 //瞬時 71 PatrickContainer container = new PatrickContainer(); 72 container.Register<IServiceA, MyServiceA>(ServiceLifetime.Transient); 73 74 75 var instance = container.Resolve<IServiceA>(); 76 var instance2 = container.Resolve<IServiceA>(); 77 78 Console.WriteLine(Object.ReferenceEquals(instance, instance2));//False 79 } 80 { 81 //作用域 82 PatrickContainer container = new PatrickContainer(); 83 container.Register<IServiceA, MyServiceA>(ServiceLifetime.Scoped); 84 85 86 var instance = container.Resolve<IServiceA>(); 87 var instance2 = container.Resolve<IServiceA>(); 88 89 Console.WriteLine(Object.ReferenceEquals(instance, instance2));//True 90 91 var container2 = container.CreateScoped(); 92 var objedd=container2.Resolve<IServiceA>(); 93 var objedd2=container2.Resolve<IServiceA>(); 94 95 /Console.WriteLine(Object.ReferenceEquals(objedd, objedd2));//True 96 97 Console.WriteLine(Object.ReferenceEquals(instance, objedd2));//False 98 Console.ReadLine(); 99 } 100 { 101 //執行緒 102 PatrickContainer container = new PatrickContainer(); 103 container.Register<IServiceA, MyServiceA>(ServiceLifetime.PerThread); 104 105 IServiceA instance = null; 106 IServiceA instance2 = null; 107 IServiceA instance3 = null; 108 IServiceA instance4 = null; 109 IServiceA instance5 = null; 110 IServiceA instance6 = null; 111 112 Task.Run(()=> { 113 Console.WriteLine($"instance,當前執行緒的ID:{Thread.CurrentThread.ManagedThreadId}"); 114 instance = container.Resolve<IServiceA>(); 115 }); 116 117 Task.Run(() => { 118 Console.WriteLine($"instance2,當前執行緒的ID:{Thread.CurrentThread.ManagedThreadId}"); 119 instance2 = container.Resolve<IServiceA>(); 120 }); 121 122 Task.Run(() => { 123 Console.WriteLine($"instance3 和 instance4,當前執行緒的ID:{Thread.CurrentThread.ManagedThreadId}"); 124 instance3 = container.Resolve<IServiceA>(); 125 instance4 = container.Resolve<IServiceA>(); 126 }); 127 128 Task.Run(() => { 129 Console.WriteLine($"instance5,當前執行緒的ID:{Thread.CurrentThread.ManagedThreadId}"); 130 instance5 = container.Resolve<IServiceA>(); 131 }).ContinueWith(t=> { 132 Console.WriteLine($"instance6,當前執行緒的ID:{Thread.CurrentThread.ManagedThreadId}"); 133 instance6 = container.Resolve<IServiceA>(); 134 }); 135 136 Thread.Sleep(1000); 137 138 Console.WriteLine(Object.ReferenceEquals(instance, instance2));//False 139 Console.WriteLine(Object.ReferenceEquals(instance, instance3));//False 140 Console.WriteLine(Object.ReferenceEquals(instance, instance4));//False 141 Console.WriteLine(Object.ReferenceEquals(instance2, instance3));//False 142 Console.WriteLine(Object.ReferenceEquals(instance2, instance4));//False 143 Console.WriteLine(Object.ReferenceEquals(instance3, instance4));//True 144 145 Console.WriteLine(Object.ReferenceEquals(instance5, instance6));//False 146 } 147 148 Console.Read(); 149 } 150 }
四、結束
好了,今天就寫到這裡了,實話實說,這個有難度嗎?其實沒什麼難度。這些程式碼都是可以直接使用的,我經過測試的,(我也有可能沒有測試到的),如果大家感覺不錯,可以拿去使用,好好的測試一下,也可以增加自己的東西,功能挺強大的,使用挺方便的。不忘初心,繼續努力。