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


上一篇說過在整個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,而ConstructorCallSiteFactoryCallSite ResultCache屬性則由構造器傳入,具體則有其服務注冊的生命周期進行實例化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;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM