I had this same problem, and the solution I came up with involves method interception. Consider the following class:
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);
}
}
Register your client class to be intercepted by this class:
var builder = new ContainerBuilder();
builder.RegisterType<InjectionInterceptor>();
builder.RegisterType<ClientClass>()
.EnableClassInterceptors()
.InterceptedBy(typeof(InjectionInterceptor));
Change your method to accept an instance of your 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
}
}
Assuming your ClientClass is resolved by Autofac, every method (that is marked as virtual) will be intercepted by this class. It will examine the method arguments and attempt to find one that is IGeneric. If the passed in argument is null, then it will examine the called method's generic type parameter, and resolve an instance of IGeneric. It will then set the argument to that resolved value.
You don't have to make the dependency a default parameter, but it allows you call your method as you would have normally, while still giving you the option of injecting a specific type if desired:
client.DoSomething<int>(); //injected by the interceptor
client.DoSomething(new Generic<int>()); // resolved manually; interceptor does nothing
The one big drawback about this method is that it will probably difficult to understand to anyone else who might be working on the code and/or debugging it, if you only ever call your DoSomething() method with an empty argument list.