通用界面:
public interface IGeneric<T>{}
客户:
public class ClientClass
{
public void DoSomething<T>()
{
//what to inject in constructor
//to get an implementation of the IGeneric<T> from autofac?
}
}
知道怎么做吗?
答案 0 :(得分:1)
假设您有一个实现界面的类
public class MyGeneric<T> : IGeneric<T>
{
}
您已在容器中注册
builder.RegisterGeneric(typeof(MyGeneric<>)).As(typeof(IGeneric<>));
然后您可以在DoSomething
方法
public class ClientClass
{
private readonly ILifetimeScope _scope;
public ClientClass(ILifetimeScope scope)
{
_scope = scope;
}
public void DoSomething<T>()
{
var myGeneric = _scope.Resolve<IGeneric<T>>();
}
}
如您所见,您需要ILifetimeScope
方法中的Autofac范围实例(DoSomething
)。您可以使用构造函数注入它。据我所知,没有别的方法,因为你的ClientClass
本身并不是通用的。您无法使用构造函数或属性注入来获取IGeneric<T>
实例,因为在创建T
实例时您不知道ClientClass
的类型。
我认为你有两种选择:
ClientClass
并使用它来解析IGeneric<T>
实例(如上所示)ClientClass
泛型并在构造函数中注入IGeneric<T>
实例答案 1 :(得分:0)
我遇到了同样的问题,我提出的解决方案涉及方法拦截。考虑以下课程:
public class InjectionInterceptor : IInterceptor {
private readonly ILifetimeScope _Scope;
public InjectionInterceptor(ILifetimeScope scope) {
_Scope = scope;
}
public void Intercept(IInvocation invocation) {
using (var lifetime = _Scope.BeginLifetimeScope(string.Format("InjectionInterceptor {0}", Guid.NewGuid()))) {
InjectDependencyIfNecessary(invocation, lifetime);
invocation.Proceed();
}
}
private void InjectDependencyIfNecessary(IInvocation invocation, ILifetimeScope lifetime) {
int indexOfDependencyArgument = FindDependencyArgument(invocation.Method);
if (indexOfDependencyArgument >= 0 && invocation.GetArgumentValue(indexOfDependencyArgument) == null) {
SetDependencyArgument(invocation, indexOfDependencyArgument, lifetime);
}
}
private static int FindDependencyArgument(System.Reflection.MethodInfo method) {
var allArgs = method.GetParameters();
return Array.FindIndex(allArgs, param =>
param.ParameterType.IsInterface &&
param.ParameterType.IsGenericType &&
param.ParameterType.GetGenericTypeDefinition() == typeof(IGeneric<>));
}
private void SetDependencyArgument(IInvocation invocation, int indexOfDependencyArgument, ILifetimeScope lifetime) {
var methodArg = invocation.Method.GetGenericArguments().Single();
var dependency = lifetime.Resolve(typeof(IGeneric<>).MakeGenericType(methodArg));
invocation.SetArgumentValue(indexOfDependencyArgument, dependency);
}
}
注册您的客户类以便被此类拦截:
var builder = new ContainerBuilder();
builder.RegisterType<InjectionInterceptor>();
builder.RegisterType<ClientClass>()
.EnableClassInterceptors()
.InterceptedBy(typeof(InjectionInterceptor));
更改您的方法以接受您的IGeneric实例:
public class ClientClass
{
public virtual void DoSomething<T>(IGeneric<T> dependency = null) //must be virtual to be intercepted
{
if (dependency == null) throw new ArgumentNullException(nameof(dependency));
//use dependency here
}
}
假设您的ClientClass由Autofac解析,则每个方法(标记为虚拟)将被此类拦截。它将检查方法参数并尝试找到一个IGeneric。如果传入的参数为null,那么它将检查被调用方法的泛型类型参数,并解析IGeneric的实例。然后它会将参数设置为该已解析的值。
您不必将依赖项设置为默认参数,但它允许您像平常一样调用方法,同时仍然可以根据需要选择注入特定类型:
client.DoSomething<int>(); //injected by the interceptor
client.DoSomething(new Generic<int>()); // resolved manually; interceptor does nothing
这种方法的一大缺点是,如果您只使用空参数列表调用DoSomething()方法,那么可能很难理解可能正在处理代码和/或调试代码的任何其他人。