DI 容器如何知道构造函数需要什么(ASP.NET 核心)?
How DI container knows what Constructors need (ASP.NET Core)?
我阅读了很多关于什么是 DI 以及如何使用它的文档(与 ASP.NET Core 相关)。据我了解,当框架为我实例化某个控制器时,它以某种方式知道该控制器的 class 需要传递给构造函数的内容。是反射还是什么?有人可以告诉我在 ASP.NET 核心 GitHub 来源上可以在哪里看到它吗?
ASP.NET 当前 RC1 上的核心 DI 的构造函数选择行为相当复杂。过去它只支持具有单个构造函数的类型,即 very good default。然而,在 RC1 中,它接受具有多个构造函数的类型。尽管如此,它的行为还是很奇怪,在测试期间,我没有设法让 DI 容器为我创建一个具有多个构造函数的组件。
在幕后,构造函数的选择和构造函数参数的分析都是使用反射完成的,表达式树被构建并最终编译成委托。代码很简单 this:
public Expression Build(Expression provider)
{
var parameters = _constructorInfo.GetParameters();
return Expression.New(
_constructorInfo,
_parameterCallSites.Select((callSite, index) =>
Expression.Convert(
callSite.Build(provider),
parameters[index].ParameterType)));
}
您可以在 GitHub 上开始寻找 here。
简而言之 shell 它使用反射来检查类型的 public 构造函数及其参数。
var constructors = implementationType.GetTypeInfo()
.DeclaredConstructors
.Where(constructor => constructor.IsPublic)
.ToArray();
它根据参数长度对构造函数进行排序,然后选择最好的一个。
此代码片段寻找最佳构造函数来调用被实例化的类型。
private ServiceCallSite CreateConstructorCallSite(ResultCache lifetime, Type serviceType, Type implementationType,
CallSiteChain callSiteChain)
{
try
{
callSiteChain.Add(serviceType, implementationType);
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)
{
var constructor = constructors[0];
var parameters = constructor.GetParameters();
if (parameters.Length == 0)
{
return new ConstructorCallSite(lifetime, serviceType, constructor);
}
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();
var currentParameterCallSites = CreateArgumentCallSites(
serviceType,
implementationType,
callSiteChain,
parameters,
throwIfCallSiteNotFound: false);
if (currentParameterCallSites != null)
{
if (bestConstructor == null)
{
bestConstructor = constructors[i];
parameterCallSites = currentParameterCallSites;
}
else
{
// Since we're visiting constructors in decreasing order of number of parameters,
// we'll only see ambiguities or supersets once we've seen a 'bestConstructor'.
if (bestConstructorParameterTypes == null)
{
bestConstructorParameterTypes = new HashSet<Type>(
bestConstructor.GetParameters().Select(p => p.ParameterType));
}
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
{
Debug.Assert(parameterCallSites != null);
return new ConstructorCallSite(lifetime, serviceType, bestConstructor, parameterCallSites);
}
}
finally
{
callSiteChain.Remove(serviceType);
}
}
我阅读了很多关于什么是 DI 以及如何使用它的文档(与 ASP.NET Core 相关)。据我了解,当框架为我实例化某个控制器时,它以某种方式知道该控制器的 class 需要传递给构造函数的内容。是反射还是什么?有人可以告诉我在 ASP.NET 核心 GitHub 来源上可以在哪里看到它吗?
ASP.NET 当前 RC1 上的核心 DI 的构造函数选择行为相当复杂。过去它只支持具有单个构造函数的类型,即 very good default。然而,在 RC1 中,它接受具有多个构造函数的类型。尽管如此,它的行为还是很奇怪,在测试期间,我没有设法让 DI 容器为我创建一个具有多个构造函数的组件。
在幕后,构造函数的选择和构造函数参数的分析都是使用反射完成的,表达式树被构建并最终编译成委托。代码很简单 this:
public Expression Build(Expression provider)
{
var parameters = _constructorInfo.GetParameters();
return Expression.New(
_constructorInfo,
_parameterCallSites.Select((callSite, index) =>
Expression.Convert(
callSite.Build(provider),
parameters[index].ParameterType)));
}
您可以在 GitHub 上开始寻找 here。
简而言之 shell 它使用反射来检查类型的 public 构造函数及其参数。
var constructors = implementationType.GetTypeInfo()
.DeclaredConstructors
.Where(constructor => constructor.IsPublic)
.ToArray();
它根据参数长度对构造函数进行排序,然后选择最好的一个。
此代码片段寻找最佳构造函数来调用被实例化的类型。
private ServiceCallSite CreateConstructorCallSite(ResultCache lifetime, Type serviceType, Type implementationType,
CallSiteChain callSiteChain)
{
try
{
callSiteChain.Add(serviceType, implementationType);
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)
{
var constructor = constructors[0];
var parameters = constructor.GetParameters();
if (parameters.Length == 0)
{
return new ConstructorCallSite(lifetime, serviceType, constructor);
}
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();
var currentParameterCallSites = CreateArgumentCallSites(
serviceType,
implementationType,
callSiteChain,
parameters,
throwIfCallSiteNotFound: false);
if (currentParameterCallSites != null)
{
if (bestConstructor == null)
{
bestConstructor = constructors[i];
parameterCallSites = currentParameterCallSites;
}
else
{
// Since we're visiting constructors in decreasing order of number of parameters,
// we'll only see ambiguities or supersets once we've seen a 'bestConstructor'.
if (bestConstructorParameterTypes == null)
{
bestConstructorParameterTypes = new HashSet<Type>(
bestConstructor.GetParameters().Select(p => p.ParameterType));
}
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
{
Debug.Assert(parameterCallSites != null);
return new ConstructorCallSite(lifetime, serviceType, bestConstructor, parameterCallSites);
}
}
finally
{
callSiteChain.Remove(serviceType);
}
}