城堡温莎: 我如何将所有接口实现注入到构造函数中?

29

我编写了一个接口,由多个类实现。我想编写一个Service类,将所有注册的实现注入其构造函数中。

我能想到的唯一解决方案是在构造函数中调用服务定位器并要求它解析所有实现。

理想情况下,我希望像这样实现-

interface IVehicle
{
    void Start();
}

class Car : IVehicle
{
    public void Start()
    {
        Console.WriteLine("Car started.");
    }
}

class Truck : IVehicle
{
    public void Start()
    {
        Console.WriteLine("Truck started.");
    }
}

class Motorbike : IVehicle
{
    public void Start()
    {
        Console.WriteLine("Motorbike started.");
    }
}

class VehicleService
{
    // How do I inject all implementations of IVehicle?
    public VehicleService(IEnumerable<IVehicle> vehicles)
    {
        foreach (var vehicle in vehicles)
        {
            vehicle.Start();
        }
    }
}

编辑 - 我应该提到我正在使用Castle Windsor。

4个回答

34

你需要使用CollectionResolver。请查看Castle Windsor FAQ:

默认情况下,当你依赖于IFoo[]、IEnumerable或IList时,Windsor会检查是否已经为该确切类型(IFoo数组或列表)注册了组件,而不是是否已经为IFoo注册了任何组件(组件数组与组件本身不同)。你可以更改其行为以表示“当你看到IFoo数组或列表时,请给我所有可获取的IFoo”,这时你需要使用CollectionResolver。

Castle Resolvers的直接链接:Resolvers


33

我知道这个问题已经有答案了,但是我认为提供一个如何添加CollectionResolver的示例会很有用,所以在这里提供一下。

在注册容器中的组件之前调用AddSubResolver方法,例如:

container = new WindsorContainer();
container.Kernel.Resolver.AddSubResolver(new CollectionResolver(container.Kernel));

按照正常流程注册组件:

container.Register(
    Component.For<IVehicle>().ImplementedBy<Car>(),
    Component.For<IVehicle>().ImplementedBy<Truck>(),
    Component.For<IVehicle>().ImplementedBy<Motorbike>()
);

很好的例子。如果有人收到“未找到System.Collections.Generic.List`1[IFoobar]的处理程序”错误消息,则可能是由于构造函数需要一个已实现的ICollection实例,例如public FoobarConstructor(List<IFoobar> ifoobarImplementations),但应该是public FoobarConstructor(IEnumerable<IFoobar> ifoobarImplementations)。 - Morten Nørgaard

22

虽然我仍然不知道如何注册给定接口的所有实现,但我觉得这个答案很有用。

希望能帮到你!

container.Register(
            Classes.FromAssemblyContaining<IVehicle>()
                .BasedOn<IVehicle>().WithService.FromInterface()
            );

这个答案不能被接受,因为它解决的是不同的问题,但它非常有帮助。与被接受的答案结合起来,它是一个干净的解决方案。 - 321X
这对我不起作用。在使用container.Register(Component.For<VehicleService>().ImplementedBy<VehicleService>())注册了VehicleService之后,我将手动注册(Component.For<IVehicle>().ImplementedBy<Car>())替换为Classes.FromAssemblyContaining<IVehicle>().BasedOn<IVehicle>().WithService.FromInterface(),但当我执行container.Resolve<VehicleService>()时会引发异常:"Blah blah blah... 'WindsorTest.Program+VehicleService'正在等待以下依赖项:-未注册的服务'....IEnumerable`1[[...]]'。" - Qwertie
我在将近5年前发布了这个项目;自那时以来,该项目已经经历了两个重大版本。尝试使用版本3.3.0进行此示例,并查看是否有效;这应该可以告诉您是您的问题还是Castle Windsor中的破坏性更改。 - CountZero
还要检查下面的答案 - 你是否添加了子解析器行?container.Kernel.Resolver.AddSubResolver(new CollectionResolver(container.Kernel)); - CountZero
1
是的,在注册之前,我正在添加“CollectionResolver”。我发现问题所在:它仅在类标记为“public”(在Windsor 5.0.1中)时才起作用(请注意OP的类不是public)。 - Qwertie

4
这里的答案都是正确的,但是我想额外补充一个小细节:你必须在注册组件之前添加子解析器 这将起作用:
container = new WindsorContainer();
container.Kernel.Resolver.AddSubResolver(new CollectionResolver(container.Kernel));
container.Register(
    Component.For<IVehicle>().ImplementedBy<Car>(),
    Component.For<IVehicle>().ImplementedBy<Truck>(),
    Component.For<IVehicle>().ImplementedBy<Motorbike>()
);

这将不会起作用。
container = new WindsorContainer();
container.Register(
    Component.For<IVehicle>().ImplementedBy<Car>(),
    Component.For<IVehicle>().ImplementedBy<Truck>(),
    Component.For<IVehicle>().ImplementedBy<Motorbike>()
);
container.Kernel.Resolver.AddSubResolver(new CollectionResolver(container.Kernel));

在调用AddSubResolver之后,应该注册接口实现(参考CountZero)。 - ItsAllABadJoke
如何在配置 XML 文件中注册时定义 CollectionResolver? - constructor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接