通過上一篇的介紹我們應該對實現在ServiceProvider的總體設計有了一個大致的瞭解,但是我們刻意迴避一個重要的話題,即服務實例最終究竟是採用何種方式提供出來的。ServiceProvider最終採用何種方式提供我們所需的服務實例取決於最終選擇了怎樣的ServiceCallSite,而服務註冊 ...
通過上一篇的介紹我們應該對實現在ServiceProvider的總體設計有了一個大致的瞭解,但是我們刻意迴避一個重要的話題,即服務實例最終究竟是採用何種方式提供出來的。ServiceProvider最終採用何種方式提供我們所需的服務實例取決於最終選擇了怎樣的ServiceCallSite,而服務註冊是採用的ServiceDescriptor有決定了ServiceCallSite類型的選擇。我們將眾多不同類型的ServiceCallSite大體分成兩組,一組用來創建最終的服務實例,另一類則與生命周期的管理有關。
一、用於服務創建的ServiceCallSite
服務實例的創建方式主要有三種,分別對應ServiceDescriptor如下三個只讀屬性。簡單來說,如果ImplementationInstance屬性返回一個具體的對象,該對象將直接作為提供的服務實例。如果屬性ImplementationFactory返回一個具體的委托對象,該委托將會作為提供服務實例的工廠。除此之外,ServiceProvider將會利用ImplementationType屬性返回的真是服務類型定位某一個最佳的構造函數來創建最終提供的服務實例。
1: public class ServiceDescriptor
2: {
3: public Type ImplementationType { get; }
4: public object ImplementationInstance { get; }
5: public Func<IServiceProvider, object> ImplementationFactory { get; }
6: }
服務實例的這三種不同的創建方式最終由三種對應的ServiceCallSite類型來完成,我們將它們的類型分別命名為InstanceCallSite、FactoryCallSite和ConstructorCallSite。如下麵的代碼片段所示,前兩種ServiceCallSite(InstanceCallSite和FactoryCallSite)的實現非常簡單,所以我們在這裡就不對它們多做介紹了。
1: internal class InstanceCallSite : IServiceCallSite
2: {
3: public object Instance { get; private set; }
4:
5: public InstanceCallSite(object instance)
6: {
7: this.Instance = instance;
8: }
9: public Expression Build(Expression provider)
10: {
11: return Expression.Constant(this.Instance);
12: }
13: public object Invoke(ServiceProvider provider)
14: {
15: return Instance;
16: }
17: }
18:
19: internal class FactoryCallSite : IServiceCallSite
20: {
21: public Func<IServiceProvider, object> Factory { get; private set; }
22: public FactoryCallSite(Func<IServiceProvider, object> factory)
23: {
24: this.Factory = factory;
25: }
26: public Expression Build(Expression provider)
27: {
28: Expression<Func<IServiceProvider, object>> factory = p => this.Factory(p);
29: return Expression.Invoke(factory, provider);
30: }
31: public object Invoke(ServiceProvider provider)
32: {
33: return this.Factory(provider);
34: }
35: }
以執行指定構造函數創建服務實例的ConstructorCallSite稍微複雜一點。如下麵的代碼片段所示,我們在創建一個ConstructorCallSite對象的時候除了指定一個代表構造函數的ConstructorInfo對象之外,還需要指定一組用於初始化對應參數列表的ServiceCallSite。
1: internal class ConstructorCallSite : IServiceCallSite
2: {
3: public ConstructorInfo ConstructorInfo { get; private set; }
4: public IServiceCallSite[] Parameters { get; private set; }
5:
6: public ConstructorCallSite(ConstructorInfo constructorInfo, IServiceCallSite[] parameters)
7: {
8: this.ConstructorInfo = constructorInfo;
9: this.Parameters = parameters;
10: }
11:
12: public Expression Build(Expression provider)
13: {
14: ParameterInfo[] parameters = this.ConstructorInfo.GetParameters();
15: return Expression.New(this.ConstructorInfo, this.Parameters.Select((p, index) => Expression.Convert(p.Build(provider),
16: parameters[index].ParameterType)).ToArray());
17: }
18:
19: public object Invoke(ServiceProvider provider)
20: {
21: return this.ConstructorInfo.Invoke(this.Parameters.Select(p => p.Invoke(provider)).ToArray());
22: }
23: }
雖然ConstructorCallSite自身創建服務實例的邏輯很簡單,但是如何創建ConstructorCallSite對象本身相對麻煩一些,因為這涉及到如何選擇一個最終構造函數的問題。我們在上面專門介紹過這個問題,並且總結出選擇構造函數採用的兩條基本的策略:
- ServiceProvider能夠提供構造函數的所有參數。
- 目標構造函數的參數類型集合是所有有效構造函數參數類型集合的超級。
我們將ConstructorCallSite的創建定義在Service類的CreateConstructorCallSite方法中,它具有額外兩個輔助方法GetConstructor和GetParameterCallSites,前者用於選擇正確的構造函數,後者則為指定的構造函數創建用於初始化參數的ServiceCallSite列表。
1: internal class Service : IService
2: {
3: private ConstructorCallSite CreateConstructorCallSite(ServiceProvider provider, ISet<Type> callSiteChain)
4: {
5: ConstructorInfo constructor = this.GetConstructor(provider, callSiteChain);
6: if (null == constructor)
7: {
8: throw new InvalidOperationException("No avaliable constructor");
9: }
10: return new ConstructorCallSite(constructor, constructor.GetParameters().Select(p => provider.GetServiceCallSite(p.ParameterType, callSiteChain)).ToArray());
11: }
12:
13: private ConstructorInfo GetConstructor(ServiceProvider provider, ISet<Type> callSiteChain)
14: {
15: ConstructorInfo[] constructors = this.ServiceDescriptor.ImplementationType.GetConstructors()
16: .Where(c => (null != this.GetParameterCallSites(c, provider, callSiteChain))).ToArray();
17:
18: Type[] allParameterTypes = constructors.SelectMany(
19: c => c.GetParameters().Select(p => p.ParameterType)).Distinct().ToArray();
20:
21: return constructors.FirstOrDefault(
22: c => new HashSet<Type>(c.GetParameters().Select(p => p.ParameterType)).IsSupersetOf(allParameterTypes));
23: }
24:
25: private IServiceCallSite[] GetParameterCallSites(ConstructorInfo constructor,ServiceProvider provider,ISet<Type> callSiteChain)
26: {
27: ParameterInfo[] parameters = constructor.GetParameters();
28: IServiceCallSite[] serviceCallSites = new IServiceCallSite[parameters.Length];
29:
30: for (int index = 0; index < serviceCallSites.Length; index++)
31: {
32: ParameterInfo parameter = parameters[index];
33: IServiceCallSite serviceCallSite = provider.GetServiceCallSite(
34: parameter.ParameterType, callSiteChain);
35: if (null == serviceCallSite && parameter.HasDefaultValue)
36: {
37: serviceCallSite = new InstanceCallSite(parameter.DefaultValue);
38: }
39: if (null == serviceCallSite)
40: {
41: return null;
42: }
43: serviceCallSites[index] = serviceCallSite;