因为它不支持开箱即用的特定构造函数的注册
Simple Injector 不支持这个是有充分理由的。您应该更喜欢让您的组件有一个公共构造函数。拥有多个构造函数被认为是一种反模式。
如果由于某种原因您不能遵循此建议,这里有一些替代方案。
一个简单的解决方案是在您的Composition Root中创建一个新类型,该类型继承自开放的泛型类型并仅定义一个构造函数。您可以改为注册该类型:
// new sub type
private sealed class SingleCtorOpenType<T> : OpenType<T>
{
public SingleCtorOpenType(IDep1 dep1, IDep2 dep2)
: base(dep1, dep2) { }
}
// registration
container.RegisterOpenGeneric(
typeof(IOpenType<>),
typeof(SingleCtorOpenType<>));
如果您只处理一种类型,这是最实用的解决方案。如果您有许多具有多个构造函数的类型,则覆盖容器的构造函数解析行为会更好。您可以为开放的泛型类型编写自定义IConstructorResolutionBehavior
实现:
public class SpecialConstructorResolutionBehavior
: IConstructorResolutionBehavior
{
private IConstructorResolutionBehavior original;
public SpecialConstructorResolutionBehavior(
IConstructorResolutionBehavior original)
{
this.original = original;
}
public ConstructorInfo GetConstructor(Type serviceType,
Type implementationType)
{
if (serviceType.IsGenericType &&
serviceType.GetGenericTypeDefinition() == typeof(IOpenType<>))
{
// do alternative constructor selection here for open types.
// For instance:
return (
from ctor in implementationType.GetConstructors()
orderby ctor.GetParameters().Length descending
select ctor)
.First();
}
// fall back to default behavior
return original.GetConstructor(serviceType, implementationType);
}
}
您可以按如下方式注册:
container.Options.ConstructorResolutionBehavior =
new SpecialConstructorResolutionBehavior(
container.Options.ConstructorResolutionBehavior);
更新
如果您想为Expression
扩展方法提供一个强制选择特定构造函数的方法,您可以创建一个扩展方法,除了为您Expression<Func<object>>
提取的之外NewExpression
,如下所示:
public static void RegisterOpenGeneric(this Container container,
Type openGenericServiceType,
Type openGenericImplementation,
Expression<Func<object>> constructorSelector)
{
var constructor =
((NewExpression)constructorSelector.Body).Constructor;
}
您可以按如下方式调用此扩展方法:
container.RegisterOpenGeneric(typeof(IList<>), typeof(List<>),
constructorSelector: () => new List<int>());
但现在麻烦开始了。传入的构造函数是 from List<int>
,但我们应该能够得到 any List<T>
,因此您必须将其转换List<int>.ctor()
为您正在解析的类型的构造函数。据我所知,.NET 缺少方便的辅助方法,例如重载ConstructorInfo.GetGenericMethodDefinition()
和ConstructorInfo.MakeGenericMethod()
重载,因此您需要进行一些查询和摆弄才能找到正确的构造函数。
IConstructorResolutionBehavior
如果您将此模型与可扩展性机制集成,这可能是最简单的,因为RegisterOpenGeneric
方法调用该接口。
因此,您的解决方案可能如下所示:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using SimpleInjector;
using SimpleInjector.Advanced;
using SimpleInjector.Extensions;
public static class OpenGenericConstructorExtensions
{
public static void EnableOpenGenericConstructorSelection(
this ContainerOptions options)
{
var selectors = new List<Func<Type, ConstructorInfo>>();
options.ConstructorResolutionBehavior =
new ConstructorResolutionBehavior(
options.ConstructorResolutionBehavior, selectors);
options.Container.SetItem("selectors", selectors);
}
public static void RegisterOpenGeneric(this Container container,
Type openGenericServiceType,
Type openGenericImplementation,
Expression<Func<object>> constructorSelector)
{
var selectors = (List<Func<Type, ConstructorInfo>>)
container.GetItem("selectors");
if (selectors == null) throw new InvalidOperationException(
"call Options.EnableOpenGenericConstructorSelection first.");
var constructor =
((NewExpression)constructorSelector.Body).Constructor;
selectors.Add(type =>
{
if (type == openGenericImplementation || (type.IsGenericType &&
type.GetGenericTypeDefinition() == openGenericImplementation))
{
return GetConstructorForType(type, constructor);
}
return null;
});
container.RegisterOpenGeneric(openGenericServiceType,
openGenericImplementation);
}
private static ConstructorInfo GetConstructorForType(
Type closedImplementationType,
ConstructorInfo closedConstructor)
{
var parameters = closedConstructor.GetParameters();
var constructors =
from ctor in closedImplementationType.GetConstructors()
where ctor.GetParameters().Length == parameters.Length
let parameterPairs = ctor.GetParameters().Zip(parameters, (p1, p2) =>
new { left = p1.ParameterType, right = p2.ParameterType })
where parameterPairs.All(pair => pair.left == pair.right ||
(pair.left.IsGenericType && pair.right.IsGenericType &&
pair.left.GetGenericTypeDefinition() == pair.right.GetGenericTypeDefinition()))
select ctor;
return constructors.Single();
}
private sealed class ConstructorResolutionBehavior
: IConstructorResolutionBehavior
{
private readonly IConstructorResolutionBehavior original;
private readonly List<Func<Type, ConstructorInfo>> constructorSelectors;
public ConstructorResolutionBehavior(
IConstructorResolutionBehavior original,
List<Func<Type, ConstructorInfo>> constructorSelectors)
{
this.original = original;
this.constructorSelectors = constructorSelectors;
}
public ConstructorInfo GetConstructor(Type serviceType,
Type implementationType)
{
var constructors =
from selector in this.constructorSelectors
let constructor = selector(implementationType)
where constructor != null
select constructor;
return constructors.FirstOrDefault() ??
this.original.GetConstructor(serviceType,
implementationType);
}
}
}
由于需要自定义IConstructorResolutionBehavior
实现,因此您将始终必须调用EnableOpenGenericConstructorSelection
上面示例中定义的。这就是代码的使用方式: var container = new Container();
var container = new Container();
container.Options.EnableOpenGenericConstructorSelection();
container.RegisterOpenGeneric(typeof(IList<>), typeof(List<>),
constructorSelector: () => new List<int>());
var list = container.GetInstance<IList<string>>();
var listOfObject = container.GetInstance<IList<object>>();