Using reflection to get generic MethodInfo without using a string name and the generic parameters are resolved at runtime
I am writing a sub dependency resolver for castle windsor. The resolver returns an object that implements a generic interface. The generic parameters are resolved at runtime and factory is used to return the correct implementation. I do not want to use a string to get the MethodInfo of the factory method. The following works but I feel that there must be a better way of resolving the factory create method, see GetMethodName and how it is used.
public class FooFactoryResolver : ISubDependencyResolver
{
private static string factoryMethodName;
private readonly IWindsorContainer container;
public FooFactoryResolver ( IWindsorContainer container )
{
this.container = container;
}
private static string GetMethodName()
{
if (factoryMethodName == null)
{
IFooFactory fooFactory = null;
Expression<Func<IFoo<object, object>>> expression =
() => fooFactory .CreateFoo<object, object>();
factoryMethodName = ( (MethodCallExpression)expression.Body ).
Method.Name;
}
return factoryMethodName;
}
public object Resolve(CreationContext context,
ISubDependencyResolver contextHandlerResolver,
Castle.Core.ComponentModel model, DependencyModel dependency)
{
return
TryToResolveDirectly( dependency ) ??
TryToResolveUsingFactories(dependency) ??
ComponentNotFound(dependency);
}
private static object ComponentNotFound(DependencyModel dependency)
{
throw new ComponentNotFoundException(dependency.TargetType);
}
private object TryToResolveUsingFactories(DependencyModel dependency)
{
var fooFactories = this.container.ResolveAll<IFooFactory>();
Type[] genericTypes = dependency.TargetItemType.
GetGenericArguments().ToArray();
return ( from fooFactory in fooFactories
开发者_运维技巧 where fooFactory.CanCreate( genericTypes[0],
genericTypes[1] )
let factoryMethod = fooFactory.GetType().
GetMethod( GetMethodName() )
select factoryMethod.MakeGenericMethod(
genericTypes.ToArray() ).
Invoke( fooFactory, new object[0] ) ).
FirstOrDefault();
}
private object TryToResolveDirectly(DependencyModel dependency)
{
return this.container.Kernel.HasComponent(dependency.TargetType) ?
this.container.Resolve( dependency.TargetType ) : null;
}
public bool CanResolve(CreationContext context,
ISubDependencyResolver contextHandlerResolver,
Castle.Core.ComponentModel model, DependencyModel dependency)
{
return dependency.TargetType.GetGenericTypeDefinition() ==
typeof( IFoo<,> );
}
}
public interface IFoo<T1, T2> { }
public interface IFooFactory
{
IFoo<T1, T2> CreateFoo<T1, T2>();
bool CanCreate(Type a, Type b);
}
I'm not sure if this is abuse or not but it gets the job done, I just feel that I am missing something obvious. I was hoping that there would be some way to change the generic parameters on the MethodInfo from the MethodCallExpression or a way to go back from the MethodInfo to it's 'Parent' and call MakeGenericMethod on that using the types that I want.
Create a custom attribute which you apply to the CreateFoo method and then use Reflection to look for that attribute on the interface type (you will always find it). This prevents having to create the Expression and you can just look for your attribute. Once you find the MethodInfo it is on, you can get the name from there.
There's no need to get a closed type declaration for IFooFactory.CreateFoo<T1, T2>()
; you are simply trying to get the name, closing the method declarations with types doesn't change the name.
精彩评论