1

我有这个代码:

using (container.BeginLifetimeScope())
{
    RenderWord instruction = new RenderWord();
    var instances = container.GetAllInstances<IInstructionHandler<RenderWord>>();
    var firstInstance = result.First();
}

instances是类型IEnumerable<IInstructionHandler<RenderWord>>

firstInstance是一种类型IInstructionHandler<RenderWord>,实际上是一个装饰器的实例,它装饰另一个装饰器,另一个装饰器装饰另一个装饰器......

在运行时,实际的类instances是类型ContainerControlledCollection<IInstructionHandler<RenderWord>>,并且这个ContainerControlledCollection类拥有一个非常有用的信息——底层ImplementationType

有什么方法可以让我在运行时到达ContainerControlledCollectionproducers[0].Value.ImplementationType,因为我真的很想能够发现装饰器链下的基本实现类型。

4

2 回答 2

2

我认为@atomaras 可能对您的抽象有一个很好的观点,尽管我认为当您只在您的合成根中使用此信息时会很好,因为您的合成根已经知道系统中的每个实现。

我认为有几种方法可以获取此信息:

  1. 使用DecoratorPredicateContext提供给RegisterDecorator扩展方法的信息:

    var typeMapping = new Dictionary<Type, Type>();
    
    container.RegisterDecorator(typeof(IInstructionHandler<>), typeof(FakeDecorator<>), c =>
    {
        typeMapping[c.ServiceType]  = c.ImplementationType;
        // or perhaps even use c.AppliedDecorators property to see which decorators 
        // are applied.
    
        // return false to prevent the decorator from being applied.
        return false;
    });
    

    您可以进行虚假注册,Simple Injector 将调用IInstructionHandler<T>系统中的每个,但您可以通过提供始终返回的谓词来防止它被应用false。您可以使用 Simple Injector 提供的信息DecoratorPredicateContext来找出实际的 ImplementationType 是什么。

或者,您可以将DecoratorContext实例(v2.6 及更高版本)注入到最顶层的装饰器中(如此所述)。包含与DecoratorContextdos 相同的信息DecoratorPredicateContext,但此对象将由 Simple Injector 自动注入到依赖的装饰器中。它允许您在装饰器中做出决定,这在您的情况下可能非常方便。

  1. 向系统添加一个IDecorator抽象以允许遍历装饰器链。

    通过让每个装饰器实现一个IDecorator允许访问被装饰者的接口(就像这里所做的那样),您可以遍历装饰器链并找到实际的实现类型:

    public interface IDecorator
    {
        object Decoratee { get; }
    }
    
    public static class DecoratorHelpers
    {
        public static IEnumerable<object> GetDecoratorChain(IDecorator decorator)
        {
            while (decorator != null)
            {
                yield return decorator;
    
                decorator = decorator.Decoratee as IDecorator;
            }
        }
    }
    

    您可以使用此接口实现装饰器,如下所示:

    public class SomeDecorator<T> : IInstructionHandler<T>, IDecorator
    {
        private readonly IInstructionHandler<T> decoratee;
    
        public SomeDecorator(IInstructionHandler<T> decoratee)
        {
            this.decoratee = decoratee;
        }
    
        object IDecorator.Decoratee { get { return this.decoratee; } }
    }
    

    当您在所有装饰器上实现此接口时,您将能够执行以下操作:

     var implementationTypes =
         from handler in container.GetAllInstances<IInstructionHandler<RenderWord>>()
         let mostInnerDecorator =
             DecoratorHelpers.GetDecoratorChain(handler as IDecorator).LastOrDefault()
         let implementation = mostInnerDecorator != null ? mostInnerDecorator.Decoratee : handler
         select implementation.GetType()
    
  2. 在其中一个重载中注册Registration实例列表,因为对象知道实际的实现类型。RegisterAllRegistration

  3. 但是,您不妨使用用于创建这些注册的实现类型列表,而不是第 3 点:

    typeMapping[serviceType] = implementationTypes;
    container.RegisterAll(serviceType, implementationTypes);
    

    Simple Injector 将始终按照注册的顺序解析注册的实现(这是有保证的)。因此,当您解决一组事物时,您将已经拥有按相同顺序排列的实现列表。

于 2013-11-07T09:10:00.210 回答
1

为什么不只检查 firstInstance 的类型?那不会给你实际的实现类型吗?我不得不说,您需要知道实现类型这一事实很好地表明了您的抽象存在问题。

于 2013-11-07T07:36:20.423 回答