Использование отражения для получения универсального MethodInfo без использования строкового имени, а универсальные параметры разрешаются во время выполнения - PullRequest
1 голос
/ 08 декабря 2010

Я пишу решатель зависимостей для замка Виндзор. Средство распознавания возвращает объект, который реализует универсальный интерфейс. Общие параметры разрешаются во время выполнения, а фабрика используется для возврата правильной реализации. Я не хочу использовать строку, чтобы получить MethodInfo фабричного метода. Следующее работает, но я чувствую, что должен быть лучший способ разрешения фабричного метода создания, см. GetMethodName и как он используется.

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);
}

Я не уверен, является ли это оскорблением или нет, но это делает работу, я просто чувствую, что упускаю что-то очевидное. Я надеялся, что будет какой-то способ изменить универсальные параметры в MethodInfo из MethodCallExpression или способ вернуться из MethodInfo в его 'Parent' и вызвать MakeGenericMethod для этого, используя типы, которые я хочу.

1 Ответ

0 голосов
/ 08 декабря 2010

Создайте пользовательский атрибут, который вы применяете к методу CreateFoo, а затем используйте Reflection для поиска этого атрибута в типе интерфейса (вы всегда найдете его).Это предотвращает необходимость создания выражения, и вы можете просто искать свой атрибут.Как только вы обнаружите, что MethodInfo включен, вы можете получить имя оттуда.

Нет необходимости получать объявление закрытого типа для IFooFactory.CreateFoo<T1, T2>();вы просто пытаетесь получить имя, закрывая объявления методов с типами , не меняя имя .

...