Core官方DI解析(3)-ServiceCallSite.md

莫問今朝·發表於2018-11-28

上一篇說過在整個DI框架中IServiceProviderEngine是核心,但是如果直接看IServiceProviderEngine派生類其實看不出也沒什麼東西,因為這個型別其實都是呼叫的其它物件方法,所以我們先來看看其它的型別

ServiceCallSite

ServiceCallSite

​ 這個是一個服務訪問配置的型別,DI內部使用此類的派生型別進行封裝所需要例項化的資訊然後進行例項化服務物件,首先我們先來看一下ServiceCallSite這個類所擁有的屬性。從下面可以看到ServiceCallSite具有三個抽象屬性和一個非抽象屬性,其中ServiceTypeImplementationType已經知道代表註冊的服務型別和例項物件的型別,

Kind是一個CallSiteKind列舉型別,代表的是當前CallSite所屬的型別,,而Cache屬性代表著服務例項物件的快取配置

internal abstract class ServiceCallSite
{
     protected ServiceCallSite(ResultCache cache)
     {
          Cache = cache;
     }
     //      當前註冊的服務型別
     public abstract Type ServiceType { get; }
     //        當前註冊的例項化型別
     public abstract Type ImplementationType { get; }
     //      當前CallSite所屬的型別
     public abstract CallSiteKind Kind { get; }
    //     服務例項物件的快取配置
     public ResultCache Cache { get; }
}

ResultCache和ServiceCacheKey型別

internal struct ResultCache
{
     //     預設ResultCache  
     public static ResultCache None { get; } = new ResultCache(CallSiteResultCacheLocation.None, ServiceCacheKey.Empty);
     internal ResultCache(CallSiteResultCacheLocation lifetime, ServiceCacheKey cacheKey)
     {
          Location = lifetime;
          Key = cacheKey;
     }
    
     public ResultCache(ServiceLifetime lifetime, Type type, int slot)
     {
          switch (lifetime)
          {
               case ServiceLifetime.Singleton:
                    Location = CallSiteResultCacheLocation.Root;
                    break;
               case ServiceLifetime.Scoped:
                    Location = CallSiteResultCacheLocation.Scope;
                    break;
               case ServiceLifetime.Transient:
                    Location = CallSiteResultCacheLocation.Dispose;
                    break;
               default:
                    Location = CallSiteResultCacheLocation.None;
                    break;
          }
          Key = new ServiceCacheKey(type, slot);
     }
     //      當前服務例項快取位置
     public CallSiteResultCacheLocation Location { get; set; }
     ///     當前服務例項所快取的使用Key
     ///     ServiceCacheKey使用基類型別和一個solt(一個數值,每例項化同一個基類型別時使用不同的solt)
     public ServiceCacheKey Key { get; set; }
}

//  快取例項物件時使用Key 
 internal struct ServiceCacheKey: IEquatable<ServiceCacheKey>
    {
        public static ServiceCacheKey Empty { get; } = new ServiceCacheKey(null, 0);
        //      註冊服務型別
        public Type Type { get; }
        //      以IEnumerable型別解析時服務的反向索引,預設例項0
        //      相同Type時此值為++
        public int Slot { get; }
        public ServiceCacheKey(Type type, int slot)
        {
            Type = type;
            Slot = slot;
        }
        public bool Equals(ServiceCacheKey other)
        {
            return Type == other.Type && Slot == other.Slot;
        }
        public override int GetHashCode()
        {
            unchecked
            {
                return (Type.GetHashCode() * 397) ^ Slot;
            }
        }
    }

ServiceCallSite

ServiceCallSite具有6個派生型別,分別是

  • ConstantCallSite 服務註冊是以單例模式以具體例項註冊時使用
  • ConstructorCallSite 服務註冊是以型別註冊,也就是例項化物件時以建構函式例項化
  • FactoryCallSite 服務註冊是以以工廠形式
  • IEnumerableCallSite 這個時呼叫獲取當前註冊型別的所有例項,也就是GetServices()時
  • ServiceProviderCallSite 這個
  • ServiceScopeFactoryCallSite 這個是獲取子容器所使用,在Engine類中會註冊此類例項,然後獲取子類容器使用

​ 這六個派生類中ConstantCallSiteIEnumerableCallSiteServiceProviderCallSiteServiceScopeFactoryCallSite這四個類的ResultCache屬性使用的是None,而ConstructorCallSiteFactoryCallSiteResultCache屬性則由構造器傳入,具體則有其服務註冊的生命週期進行例項化ResultCache

] 在這裡看一下ConstantCallSite,ConstructorCallSite,IEnumerableCallSiteServiceScopeFactoryCallSite這四個類

ConstantCallSite

​ 既然ConstantCallSite是具體例項註冊的,所以此類中具有一個例項物件屬性,由下面程式碼可以看出在構造此類例項時傳入例項值,然後賦值給DefaultValue屬性,這個型別也是這些派生類中唯一一個擁有具體例項的,

​ 然後Kind這個屬性可以看到被賦值成了CallSiteKind.Constant,前面說過這個屬性相當於代表此型別的屬性,其它派生類都具有相應的列舉值

internal class ConstantCallSite : ServiceCallSite
    {
        /// <summary>
        ///     註冊時提供的具體例項物件值
        /// </summary>
       internal object DefaultValue { get; }

        public ConstantCallSite(Type serviceType, object defaultValue): base(ResultCache.None)
        {
            DefaultValue = defaultValue;
        }
        /// <summary>
        ///     註冊的基類型別
        /// </summary>
        public override Type ServiceType => DefaultValue.GetType();
        /// <summary>
        ///     其實際物件所對應的型別
        /// </summary>
        public override Type ImplementationType => DefaultValue.GetType();
        /// <summary>
        ///     當前ServiceCallSite所對應的型別
        /// </summary>
        public override CallSiteKind Kind { get; } = CallSiteKind.Constant;
    }

ConstructorCallSite

​ 這個類中具有兩個主要屬性

ConstructorInfo:當前選中的最優構造器

ParameterCallSites:構造引數陣列

internal class ConstructorCallSite : ServiceCallSite
{
     ///     例項化物件時所使用的構造器,當前構造器的最優構造器
     internal ConstructorInfo ConstructorInfo { get; }
     ///     當前構造器中所有引數的ServiceCallSite集合
     internal ServiceCallSite[] ParameterCallSites { get; }
     
     //     最優構造器為無參
     public ConstructorCallSite(ResultCache cache, Type serviceType, ConstructorInfo constructorInfo) : this(cache, serviceType, constructorInfo, Array.Empty<ServiceCallSite>())
     {}
     
     public ConstructorCallSite(ResultCache cache, Type serviceType, ConstructorInfo constructorInfo, ServiceCallSite[] parameterCallSites) : base(cache)
     {
          ServiceType = serviceType;
          ConstructorInfo = constructorInfo;
          ParameterCallSites = parameterCallSites;
     }
     public override Type ServiceType { get; }
     //     使用構造器的DeclaringType
     public override Type ImplementationType => ConstructorInfo.DeclaringType;
     public override CallSiteKind Kind { get; } = CallSiteKind.Constructor;
}

IEnumerableCallSite

IEnumerableCallSite前面說過是對應的獲取所有服務的訪問設定型別,從下面程式碼可以看出其實這個類就是內部維護了一個ServiceCallSite陣列和一個ItemType(這個代表真實的基類型別),並且要求例項物件時進行傳入,然後最後例項化物件時遍歷陣列即可

internal class IEnumerableCallSite : ServiceCallSite
    {
        /// <summary>
        ///     當前註冊的型別  (基類型別)
        /// </summary>
        internal Type ItemType { get; }
        /// <summary>
        ///     所有服務的ServiceCallSite陣列
        /// </summary>
        internal ServiceCallSite[] ServiceCallSites { get; }

        public IEnumerableCallSite(Type itemType, ServiceCallSite[] serviceCallSites) : base(ResultCache.None)
        {
            ItemType = itemType;
            ServiceCallSites = serviceCallSites;
        }

        public override Type ServiceType => typeof(IEnumerable<>).MakeGenericType(ItemType);
        public override Type ImplementationType  => ItemType.MakeArrayType();
        //      當前型別是IEnumberable標誌
        public override CallSiteKind Kind { get; } = CallSiteKind.IEnumerable;
    }

ServiceScopeFactoryCallSite

​ 這個型別是子容器的工廠型別,下面程式碼中看到ImplementationType是一個ServiceProviderEngine型別,其實這個引擎類不止實現了IServiceProviderEngine介面,還實現了IServiceScopeFactory

internal class ServiceScopeFactoryCallSite : ServiceCallSite
    {
        public ServiceScopeFactoryCallSite() : base(ResultCache.None)
        {
        }

        public override Type ServiceType { get; } = typeof(IServiceScopeFactory);
        //      IServiceProviderEngine派生型別,這個型別也實現了IServiceScopeFactory介面,所以是一個子容器工廠型別
        public override Type ImplementationType { get; } = typeof(ServiceProviderEngine);
        public override CallSiteKind Kind { get; } = CallSiteKind.ServiceScopeFactory;
    }

ServiceDescriptorCacheItem

​ 從下面程式碼可以看出這是一個結構,這個結構是具有相同註冊服務的所有ServiceDescriptor封裝,在CallSiteFactory類中進行使用

 private struct ServiceDescriptorCacheItem{}

​ 在此結構中,可以看到具有兩個欄位**_item屬性和一個_items集合屬性,_item屬性代表相同註冊服務的第一個ServiceDescriptor,而_items**則是除去第一個其它的ServiceDescriptor集合,我沒看懂微軟為什麼要這麼幹

**_item**:代表此註冊服務的第一個ServiceDescriptor

**_items**:此欄位表示除去第一個的的所有ServiceDescriptor集合

​ 此結構中的LastCount分別是獲取快取的最後一個元素和數量,因為第一個ServiceDescriptor是**_item屬性,所以這兩個屬性都考慮了_item**,

/// <summary>
///     獲取其註冊的最後一個ServiceDescriptor
///     如果其_items集合為空,則獲取其_item的值
/// </summary>
public ServiceDescriptor Last
{
     get
     {
          if (_items != null && _items.Count > 0)
            return _items[_items.Count - 1];
          return _item;
     }
}
//     所有相同註冊型別的數量,
//      因為第一個是_item,所以需要1+_items.Count
public int Count
{
     get
     {
        if (_item == null)
            return 0;
        return 1 + (_items?.Count ?? 0);
     }
}
public ServiceDescriptor this[int index]
{
     get
     {
          if (index >= Count)
               throw new ArgumentOutOfRangeException(nameof(index));
          if (index == 0)
               return _item;
          return _items[index - 1];
     }
}

​ 結構中只有一個And()方法,此方法是新增一個ServiceDescriptor,可以每次呼叫此方法時都會建立新的例項,

//     將指定固定ServiceDescriptor新增到集合中
//     首先例項化一個新的 ServiceDescriptorCacheItem物件
//     如果當前物件_item屬性為空,則將當前引數作為新ServiceDescriptorCacheItem物件>item屬性
//     如果當前物件_item不為空,則當前的物件_item作為新ServiceDescriptorCacheItem物件>item屬性,並且將原物件集合賦值給新物件集合,並且將引數加入到新物件集合中,然後返回新物件,
//     也就是第一個加入的永遠是_item值,其後加入的放入集合中
public ServiceDescriptorCacheItem Add(ServiceDescriptor descriptor)
{
     var newCacheItem = new ServiceDescriptorCacheItem();
     if (_item == null)
          newCacheItem._item = descriptor;
     else
     {
          newCacheItem._item = _item;
          newCacheItem._items = _items ?? new List<ServiceDescriptor>();
          newCacheItem._items.Add(descriptor);
     }
     return newCacheItem;
}

CallSiteFactory

​ 下面來看看CallSiteFactory這個型別,這是ServiceCallSite的工廠型別,內部根據ServiceDescriptor建立對應的ServiceCallSite,下面一點點來看看這個型別

下面程式碼中是CallSiteFactory類中的屬性

DefaultSlot:此屬性是預設的Slot,預設為0

_descriptors:此屬性是快取所有的ServiceDescriptor

_callSiteCache:ServiceCallSite的快取集合

_descriptorLookup:ServiceDescriptorCacheItem快取集合

internal class CallSiteFactory
{
     //      預設的Slot為0,
     private const int DefaultSlot = 0;
     ///     儲存所有註冊服務型別
     private readonly List<ServiceDescriptor> _descriptors;
     ///     ServiceCallSite快取集合
     private readonly ConcurrentDictionary<Type, ServiceCallSite> _callSiteCache = new ConcurrentDictionary<Type, ServiceCallSite>();
     
     ///     所有註冊的服務快取型別
     ///     其中以所註冊基類型別分組包裝為一個ServiceDescriptorCacheItem型別,然後以註冊的基類型別為Key進行快取
     private readonly Dictionary<Type, ServiceDescriptorCacheItem> _descriptorLookup = new Dictionary<Type, ServiceDescriptorCacheItem>();
}

​ 從下面程式碼可以看到CallSiteFactory型別建構函式需要一個IEnumerable<ServiceDescriptor> descriptors,在建構函式中除了例項化_stackGuard物件和快取_descriptors之外,還呼叫了一個Populate()方法,這個方法是初始化_descriptorLookup快取

public CallSiteFactory(IEnumerable<ServiceDescriptor> descriptors)
{
     _stackGuard = new StackGuard();
     _descriptors = descriptors.ToList();
     //      呼叫此方法快取ServiceDescriptorCacheItem
     Populate(descriptors);
}

​ 在Populate方法中,首先經過了一系列的判斷,最進行快取

private void Populate(IEnumerable<ServiceDescriptor> descriptors)
{
     foreach (var descriptor in descriptors)
     {
          //      獲取ServiceDescriptor物件中所註冊的基類
          var serviceTypeInfo = descriptor.ServiceType.GetTypeInfo();
          if (serviceTypeInfo.IsGenericTypeDefinition)
          {
               // 如果當前基類是泛型類,
               //  那麼如果其實際型別implementationTypeInfo類不是泛型類或者為抽象類,那麼就丟擲異常
               var implementationTypeInfo = descriptor.ImplementationType?.GetTypeInfo();
               if (implementationTypeInfo == null || !implementationTypeInfo.IsGenericTypeDefinition)
                    throw new ArgumentException(
                    Resources.FormatOpenGenericServiceRequiresOpenGenericImplementation(descriptor.ServiceType),
                    nameof(descriptors));

               if (implementationTypeInfo.IsAbstract || implementationTypeInfo.IsInterface)
                    throw new ArgumentException(
                    Resources.FormatTypeCannotBeActivated(descriptor.ImplementationType, descriptor.ServiceType));
          }
          else if (descriptor.ImplementationInstance == null && descriptor.ImplementationFactory == null)
          {
               //      如果當前基類不為泛型類
               //      那麼如果其實際型別為泛型類或者是抽象型別,那麼就丟擲異常
               var implementationTypeInfo = descriptor.ImplementationType.GetTypeInfo();

               if (implementationTypeInfo.IsGenericTypeDefinition ||
                   implementationTypeInfo.IsAbstract ||
                   implementationTypeInfo.IsInterface)
                    throw new ArgumentException(
                    Resources.FormatTypeCannotBeActivated(descriptor.ImplementationType, descriptor.ServiceType));
          }
          //      使用其註冊的基類為key,將此ServiceDescriptor快取到Dictionary<Type, ServiceDescriptorCacheItem>集合中
          //      ServiceDescriptorCacheItem是一個存放了所有相同註冊基類的ServiceDescriptor
          //      ServiceDescriptorCacheItem中具有一個item屬性和一個items集合
          //      item屬性是註冊的第一個此型別的ServiceDescriptor
          var cacheKey = descriptor.ServiceType;
          //        由於ServiceDescriptorCacheItem是一個結構,所以不會異常
          _descriptorLookup.TryGetValue(cacheKey, out var cacheItem);
          _descriptorLookup[cacheKey] = cacheItem.Add(descriptor);
     }
}

​ 在此類中具有一個GetCallSite()方法,外部也是呼叫此方法進行獲取ServiceCallSite,如果當前ServiceCallSite已被快取,則直接獲取快取中資料,如果未快取,則建立並快取,從下面程式碼可以看到,如果未被快取就呼叫CreateCallSite()進行建立

​ 當前函式中有一個CallSiteChain型別,這個型別是一個限制,應該是為了防止多執行緒,在建立之前進行了判斷,如果已建立,則丟擲異常,CallSiteChain這個類在此就不做介紹

internal ServiceCallSite GetCallSite(Type serviceType, CallSiteChain callSiteChain)
     => _callSiteCache.GetOrAdd(serviceType, (type, chain) => CreateCallSite(type, chain), callSiteChain);

​ 在CreateCallSite()首先呼叫了CallSiteChain例項的CheckCircularDependency()方法,這個方法就是如果已被建立,則丟擲異常.然後分別呼叫TryCreateExact(),TryCreateOpenGeneric(),TryCreateEnumerable()這三個方法進行嘗試例項化ServiceCallSite,下面我們來看看這三個方法和它們依賴的方法

private ServiceCallSite CreateCallSite(Type serviceType, CallSiteChain callSiteChain)
{
     ServiceCallSite callSite;
     try
     {
          //      檢查是否已被建立,如果已建立,則丟擲異常
          callSiteChain.CheckCircularDependency(serviceType);
          //      獲取指定服務的例項物件方式
          //          1.首先建立普通型別的ServiceCallSite,
          //          2.建立泛型型別的ServiceCallSite
          //          3.如果服務型別是集合.那麼將獲取當前型別所有實現物件
          callSite = TryCreateExact(serviceType, callSiteChain) ??
               TryCreateOpenGeneric(serviceType, callSiteChain) ??
               TryCreateEnumerable(serviceType, callSiteChain);
     }
     finally
     {
          callSiteChain.Remove(serviceType);
     }
     _callSiteCache[serviceType] = callSite;

     return callSite;
}

1.TryCreateExact()

TryCreateExact()方法是如果ServiceType只是一個普通型別時才使用的方法,如下程式碼,首先判斷了此型別是否存在於**_descriptorLookup快取中,如果不存在直接返回null,如果存在的話直接使用最後一個ServiceDescriptorDefaultSlot**進行,這也就是為什麼總是會獲取最後一個服務例項的原因

private ServiceCallSite TryCreateExact(Type serviceType, CallSiteChain callSiteChain)
{
     //      在_descriptorLookup快取中獲取指定基類的所有ServiceDescriptor例項,
     //      然後利用最後一個ServiceDescriptor進行例項化ServiceCallSite
     if (_descriptorLookup.TryGetValue(serviceType, out var descriptor))
        return TryCreateExact(descriptor.Last, serviceType, callSiteChain, DefaultSlot);
     return null;
}

TryCreateExact()中則根據註冊服務的方式進行例項化ServiceCallSite可以看到使用具體例項物件和工廠時直接例項化ServiceCallSite,而使用型別註冊時則又呼叫CreateConstructorCallSite()進行例項化一個ConstructorCallSite物件

private ServiceCallSite TryCreateExact(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain, int slot)
{
     //      判斷基類型別是否與ServiceDescriptor所持有的基類型別是否一致,如果不一致直接返回false
     if (serviceType == descriptor.ServiceType)
     {
          ServiceCallSite callSite;
          //      根據當前註冊的生命週期,基類型別和slot例項化一個ResultCache,
          //      ResultCache型別具有一個最後結果快取的位置(相當於跟生命週期一致)和一個快取Key
          var lifetime = new ResultCache(descriptor.Lifetime, serviceType, slot);
          //      根據註冊時所使用的方式來建立不同的ServiceCallSite,共具有三種ServiceCallSite子類
          //      ConstantCallSite    註冊時直接根據物件進行例項化具體物件(Singleton生命週期獨有)
          //      FactoryCallSite     註冊時根據一個工廠例項化物件
          //      ConstructorCallSite 註冊時根據具體例項型別進行例項化物件
          if (descriptor.ImplementationInstance != null)
               callSite = new ConstantCallSite(descriptor.ServiceType, descriptor.ImplementationInstance);
          else if (descriptor.ImplementationFactory != null)
               callSite = new FactoryCallSite(lifetime, descriptor.ServiceType, descriptor.ImplementationFactory);
          else if (descriptor.ImplementationType != null)
               //      如果註冊型別是使用的派生類型別方式,則呼叫CreateConstructorCallSite來例項化一個ConstructorCallSite
               callSite = CreateConstructorCallSite(lifetime, descriptor.ServiceType, descriptor.ImplementationType, callSiteChain);
          else
               throw new InvalidOperationException("Invalid service descriptor");
          return callSite;
     }
     return null;
}

​ 下面看一下CreateConstructorCallSite()這個方法,在這個方法中選擇最優構造器並例項化ConstructorCallSite物件,

首先獲取例項型別的所有公共構造器,如果不存在就丟擲異常

如果此型別只有一個構造器,那麼就使用此構造器當做最優構造器進行例項化,

如果此型別具有多個構造器,那麼就選出最優構造器

如果沒有找到最優構造器,就丟擲異常,存在最優構造器就以此構造器例項化ConstructorCallSite

注:最優構造器是引數最多的構造器,但是如果其它構造器引數中具有最優構造器沒有的引數,就丟擲異常

​ 在此方法中如果最優構造器擁有引數,還會呼叫一個CreateArgumentCallSites(),這個方法會依次例項化引數的ServiceCallSite

private ServiceCallSite CreateConstructorCallSite(ResultCache lifetime, Type serviceType, Type implementationType,CallSiteChain callSiteChain)
{
     //      將此服務型別和例項型別存入callSiteChain
     callSiteChain.Add(serviceType, implementationType);
     //      獲取例項型別的所有公共構造器,
     //      然後選擇其最優的構造器並建立ConstructorCallSite  
     var constructors = implementationType.GetTypeInfo()
          .DeclaredConstructors
          .Where(constructor => constructor.IsPublic)
          .ToArray();
     ServiceCallSite[] parameterCallSites = null;
     if (constructors.Length == 0)
          //     沒有公共構造器,直接丟擲異常
          throw new InvalidOperationException(Resources.FormatNoConstructorMatch(implementationType));
     else if (constructors.Length == 1)
     {
          //     如果當前構造器為1個,則判斷構造器是否存在引數並將所有引數進行例項化(建立指定的ServiceCallSite),
          var constructor = constructors[0];
          //      獲取當前構造器的所有引數,並對引數一一進行建立ServiceCallSite  遞迴呼叫
          var parameters = constructor.GetParameters();
          if (parameters.Length == 0)
          {
               return new ConstructorCallSite(lifetime, serviceType, constructor);
          }
          //      建立當前構造器所有引數的ServiceCallSite
          //      如果具有未知的引數,則直接丟擲異常
          parameterCallSites = CreateArgumentCallSites(
               serviceType,
               implementationType,
               callSiteChain,
               parameters,
               throwIfCallSiteNotFound: true);

          return new ConstructorCallSite(lifetime, serviceType, constructor, parameterCallSites);
     }
     //      根據構造器引數長度進行排序,判斷所有構造器中是否具有未知引數
     Array.Sort(constructors,
                (a, b) => b.GetParameters().Length.CompareTo(a.GetParameters().Length));

     //      最優構造器
     ConstructorInfo bestConstructor = null;
     HashSet<Type> bestConstructorParameterTypes = null;
     for (var i = 0; i < constructors.Length; i++)
     {
          var parameters = constructors[i].GetParameters();
          //      建立當前構造器所有引數的ServiceCallSite
          //      如果具有未知的引數,則不丟擲異常
          var currentParameterCallSites = CreateArgumentCallSites(
               serviceType,
               implementationType,
               callSiteChain,
               parameters,
               throwIfCallSiteNotFound: false);

          if (currentParameterCallSites != null)
          {
               //  如果所有引數的ServiceCallSite構造成功,並且當前最優構造器物件為空,則將當前構造器設定為最優構造器
               if (bestConstructor == null)
               {
                    bestConstructor = constructors[i];
                    parameterCallSites = currentParameterCallSites;
               }
               else
               {
                    if (bestConstructorParameterTypes == null)
                         //      如果最優引數型別集合為空,則將當前構造器的引數賦給集合
                         bestConstructorParameterTypes = new HashSet<Type>(
                         bestConstructor.GetParameters().Select(p => p.ParameterType));
                    //      如果bestConstructorParameterTypes為不為當前構造引數集合的子集,則丟擲異常
                    //      子集指當前bestConstructorParameterTypes集合中所有資料是否在當前構造引數集合之中
                    if (!bestConstructorParameterTypes.IsSupersetOf(parameters.Select(p => p.ParameterType)))
                    {
                         // Ambiguous match exception
                         var message = string.Join(
                              Environment.NewLine,
                              Resources.FormatAmbiguousConstructorException(implementationType),
                              bestConstructor,
                              constructors[i]);
                         throw new InvalidOperationException(message);
                    }
               }
          }
     }
     //      如果未找到最優建構函式,則丟擲異常
     if (bestConstructor == null)
          throw new InvalidOperationException(
          Resources.FormatUnableToActivateTypeException(implementationType));
     else
          //      例項化一個ConstructorCallSite物件並返回
          return new ConstructorCallSite(lifetime, serviceType, bestConstructor, parameterCallSites);
}

​ 在CreateArgumentCallSites()中遞迴呼叫GetCallSite()獲取每一個引數對應的ServiceCallSite,在方法中可以看到如果從GetCallSite()中未獲取到對應的例項物件但是該引數具有預設引數,那麼就使用預設引數.

​ 在這個方法有意思的是最後一個引數,最後一個引數如果為true,那麼如果最終未獲取到引數的ServiceCallSite就丟擲一場,如果為false,就返回null

private ServiceCallSite[] CreateArgumentCallSites(
     Type serviceType,
     Type implementationType,
     CallSiteChain callSiteChain,
     ParameterInfo[] parameters,
     bool throwIfCallSiteNotFound)
{
     var parameterCallSites = new ServiceCallSite[parameters.Length];
     for (var index = 0; index < parameters.Length; index++)
     {
          //      依次遞迴呼叫獲取指定引數的ServiceCallSite
          var callSite = GetCallSite(parameters[index].ParameterType, callSiteChain);
          if (callSite == null && ParameterDefaultValue.TryGetDefaultValue(parameters[index], out var defaultValue))
               //          如果獲取引數的ServiceCallSite失敗但是該引數具有預設值
               //          則直接以預設值來建立ConstantCallSite物件
               callSite = new ConstantCallSite(serviceType, defaultValue);
          //      如果當前callSite還為空,則代表出現無法例項化的引數型別
          //      如果允許丟擲異常則丟擲異常,如果不允許丟擲異常則返回null
          if (callSite == null)
          {
               if (throwIfCallSiteNotFound)
                    throw new InvalidOperationException(Resources.FormatCannotResolveService(
                         parameters[index].ParameterType,
                         implementationType));
               return null;
          }

          parameterCallSites[index] = callSite;
     }
     return parameterCallSites;
}

2.TryCreateOpenGeneric()

​ 從下面程式碼可以看出TryCreateOpenGeneric()首先會判斷此泛型是否是封閉型別並且此型別是否存在於**_descriptorLookup,然後呼叫TryCreateOpenGeneric()**進行獲取ServiceCallSite

​ 在TryCreateOpenGeneric()中則根據註冊服務型別的泛型引數製造一個實現型別引數,然後呼叫CreateConstructorCallSite()進行例項化ServiceCallSite,所以泛型只能以構造器例項方式

private ServiceCallSite TryCreateOpenGeneric(Type serviceType, CallSiteChain callSiteChain)
{
     //      如果是泛型是封閉並且在_descriptorLookup快取集合中具有此型別的快取
     if (serviceType.IsConstructedGenericType
         && _descriptorLookup.TryGetValue(serviceType.GetGenericTypeDefinition(), out var descriptor))
            return TryCreateOpenGeneric(descriptor.Last, serviceType, callSiteChain, DefaultSlot);
     return null;
}

private ServiceCallSite TryCreateOpenGeneric(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain, int slot)
{
     //   如果當前泛型型別為封閉並且當前註冊的基類型別為當前泛型的開放型別,則例項化,否則返回null
     if (serviceType.IsConstructedGenericType &&
         serviceType.GetGenericTypeDefinition() == descriptor.ServiceType)
     {
          //  利用當前註冊服務的宣告和生命週期型別例項化一個結果快取配置
          var lifetime = new ResultCache(descriptor.Lifetime, serviceType, slot);
          //     利用註冊型別泛型引數創造派生類封閉泛型型別
          var closedType = descriptor.ImplementationType.MakeGenericType(serviceType.GenericTypeArguments);
          //      建立一個ConstructorCallSite並返回
          return CreateConstructorCallSite(lifetime, serviceType, closedType, callSiteChain);
     }
     return null;
}

3.TryCreateEnumerable()

​ 最後我們來看看TryCreateEnumerable()這個方法,這個方法就是獲取IEnumerableCallSite型別的,也就是獲取當前註冊型別所有例項時使用的,從下面程式碼可以看到如果IEnumerable的泛型引數不是泛型並且快取於**_descriptorLookup集合中,就使用對應的所有的ServiceProvider進行例項化,如果二者有一不可就遍歷_descriptors**例項ServiceCallSite

private ServiceCallSite TryCreateEnumerable(Type serviceType, CallSiteChain callSiteChain)
{
     //      型別是封閉泛型型別並且泛型集合為IEnumerable
     if (serviceType.IsConstructedGenericType &&
         serviceType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
     {
          //      獲取當前註冊型別集合的泛型引數,由此型別來當基類型別進行獲取註冊當前型別的所有服務ServiceCallSite
          var itemType = serviceType.GenericTypeArguments.Single();

          callSiteChain.Add(serviceType);

          var callSites = new List<ServiceCallSite>();

          if (!itemType.IsConstructedGenericType &&
              _descriptorLookup.TryGetValue(itemType, out var descriptors))
          {
               //  如果泛型型別不是泛型並存在於快取中
               for (int i = 0; i < descriptors.Count; i++)
               {
                    //      一次獲取其中每一個ServiceDecriptor然後建立對應的ServiceCallSite
                    var descriptor = descriptors[i];
                    //  設定當前slot
                    //   slot為倒序設定
                    var slot = descriptors.Count - i - 1;
                    // There may not be any open generics here
                    //      獲取當前ServiceDecriptor的ServiceCallSite並新增陣列中
                    var callSite = TryCreateExact(descriptor, itemType, callSiteChain, slot);
                    callSites.Add(callSite);
               }
          }
          else
          {
               var slot = 0;
               for (var i = _descriptors.Count - 1; i >= 0; i--)
               {
                    //遍歷所有註冊的ServiceDescriptor並獲取對應的ServiceCallSite,然後如果不為空則新增至陣列中
                    var descriptor = _descriptors[i];
                    var callSite = TryCreateExact(descriptor, itemType, callSiteChain, slot) ??
                         TryCreateOpenGeneric(descriptor, itemType, callSiteChain, slot);
                    slot++;
                    if (callSite != null)
                         callSites.Add(callSite);
               }
               //      反轉集合元素
               callSites.Reverse();
          }
          //  例項化IEnumerableCallSite並返回
          return new IEnumerableCallSite(itemType, callSites.ToArray());
     }
     return null;
}

​ 在CallSiteFactory類中還具有一個Add(),這個方法是往**_callSiteCache**欄位新增快取ServiceCallSite

 public void Add(Type type, ServiceCallSite serviceCallSite)
      => _callSiteCache[type] = serviceCallSite;

相關文章