这实际上与
HtmlSanitizer
具体来说,更多的是与如何。NET核心构造函数依赖项注入有效。
根据
documentation
:
服务可以通过以下方式解决:
-
IServiceProvider
-
ActivatorUtilities:
-
创建未在容器中注册的对象。
-
与一些框架功能一起使用。
构造函数可以接受依赖项未提供的参数
注入,但参数必须分配默认值。
当服务由IServiceProvider或ActivatorUtilities解析时,
构造函数注入需要一个公共构造函数。
当ActivatorUtilities解析服务时,构造函数
注入只需要存在一个适用的构造函数。
支持构造函数重载,但只能存在一个重载
其参数都可以通过依赖注入来实现。
在这种情况下,您使用的是IServiceProvider,框架可以“访问”特定类型的参数
IEnumerable<T>
,这是所需的
HtmlSanitizer
constructor
:
public HtmlSanitizer(IEnumerable<string>? allowedTags = null, IEnumerable<string>? allowedSchemes = null,
IEnumerable<string>? allowedAttributes = null, IEnumerable<string>? uriAttributes = null, IEnumerable<string>? allowedCssProperties = null)
{
AllowedTags = new HashSet<string>(allowedTags ?? DefaultAllowedTags, StringComparer.OrdinalIgnoreCase);
AllowedSchemes = new HashSet<string>(allowedSchemes ?? DefaultAllowedSchemes, StringComparer.OrdinalIgnoreCase);
AllowedAttributes = new HashSet<string>(allowedAttributes ?? DefaultAllowedAttributes, StringComparer.OrdinalIgnoreCase);
UriAttributes = new HashSet<string>(uriAttributes ?? DefaultUriAttributes, StringComparer.OrdinalIgnoreCase);
AllowedCssProperties = new HashSet<string>(allowedCssProperties ?? DefaultAllowedCssProperties, StringComparer.OrdinalIgnoreCase);
AllowedAtRules = new HashSet<CssRuleType>(DefaultAllowedAtRules);
AllowedClasses = new HashSet<string>(DefaultAllowedClasses, StringComparer.OrdinalIgnoreCase);
}
当服务解析程序看到带有参数的构造函数时,它将尝试访问每个参数。假使
IEnumerable<T>
,参数是专门处理的,默认数组将根据
source
:
protected override object VisitIEnumerable(IEnumerableCallSite enumerableCallSite, RuntimeResolverContext context)
{
var array = Array.CreateInstance(
enumerableCallSite.ItemType,
enumerableCallSite.ServiceCallSites.Length);
for (int index = 0; index < enumerableCallSite.ServiceCallSites.Length; index++)
{
object value = VisitCallSite(enumerableCallSite.ServiceCallSites[index], context);
array.SetValue(value, index);
}
return array;
}
您可以用一个非常简单的测试工具来证明这一点:
public class Test : ITest
{
private ISet<string> _defaults = new HashSet<string> { "one", "two", "three" };
private ISet<string> _filters;
public Test(List<string> filters = null)
{
_filters = new HashSet<string>(filters.ToHashSet() ?? _defaults);
}
}
public interface ITest { }
在这种情况下,参数过滤器将为null,并且在解析时将使用默认值
provider.GetService(typeof(ITest));
。但是,如果我需要
IEnumerable
相反
public class Test : ITest
{
private ISet<string> _defaults = new HashSet<string> { "one", "two", "three" };
private ISet<string> _filters;
public Test(IEnumerable<string> filters = null)
{
_filters = new HashSet<string>(filters.ToHashSet() ?? _defaults);
}
}
public interface ITest { }
您会发现传递了一个默认数组,导致不使用默认的筛选器。
通过使用返回的工厂实例化
new HtmlSanitizer()
,您可以绕过此实现行为,并为每个参数传递null,从而允许使用默认值。
这是一个非常令人惊讶的行为,我找不到任何将其描述为预期行为的文档。我相信这可能只是一个疏忽。NET核心DI团队,因为通常依赖项是不可IEnumerable类型。同样值得注意的是,此行为不适用于的参数
IList<T>
或
ISet<T>
类型