Castle Windsor:强制解析器使用指定的构造函数

时间:2011-10-28 08:11:32

标签: c# .net dependency-injection castle-windsor ioc-container

以下是示例:

interface IComponentA {};

class ComponentA : IComponentA { };

interface IComponentB { };

class ComponentB : IComponentB { };

interface IComponentC { };

class ComponentC : IComponentC
{
    public ComponentC(IComponentA a)
    {
        Console.WriteLine("Constructor A"); 
    }

    public ComponentC(IComponentB b) 
    {
        Console.WriteLine("Constructor B");
    }
};

所有这些组件都在Castle Windsor容器中注册。

但是类ComponentC有2个重载的构造函数。当ComponentC被激活时,可以使用它们中的任何一个。

我需要使用ComponentC(IComponentB b)构造函数。

我正在使用UsingFactoryMethod()方法来解决这个问题:

container
    .Register(Component
        .For<IComponentA>()
        .ImplementedBy<ComponentA>())
    .Register(Component
        .For<IComponentB>()
        .ImplementedBy<ComponentB>())
    .Register(Component
        .For<IComponentC>()
        .UsingFactoryMethod(() => new ComponentC(
            container.Resolve<IComponentB>())));

它有效,但Castle Windsor可能提供了一些更好的方法吗?

非常感谢任何帮助。

感谢。

2 个答案:

答案 0 :(得分:6)

Windsor不支持这种情况,因为它打破了(和大多数容器)基于以下操作的一个不成文的假设:“所有构造函数都是相同的。”

这意味着,无论选择哪种构造函数,组件的行为都不应存在功能差异。在所有条件相同的情况下,组件拥有的依赖关系越多,它就越有能力,这就是为什么Windsor会首先选择贪婪的构造函数,但是如果像你的那样,我会说两件事情都发生了:

  • 您的组件实际上可能是伪装成一个组件的两个组件。在这种情况下,您可能希望将其拆分。
  • 你的组件实际上确实使用它所具有的两个依赖项运行,因此它应该有一个构造函数可以同时使用它们。

我见过的另一种情况是:

public class Foo
{
   public Foo(ISession session){/*code*/}
   public Foo(ISessionFactory factory):this(factory.OpenSession()){}
}

虽然这看起来似乎是一个聪明的主意,但充其量只是多余,混乱和不必要的。如果你的情况看起来像这个,我只是删除第二个构造函数。

答案 1 :(得分:1)

嗯,这很糟糕,但有一种方法(我过去必须使用Linq2Sql DataContext对象)。您可以创建一个装饰器类并注册它。

我们假设你有这个界面:

public interface IService 
{
    void DoSomething();
}

您的实施如下:

public class Service : IService
{
    private readonly ILogger _logger;

    public Service(ILogger logger)
        : this(logger, SomeDefaultListOfThings())
    {
    }

    // Let's say Windsor is calling this ctor for some reason (ArrayResolver?)
    public Service(ILogger logger, IEnumerable<object> emptyArrayFromWindsor)
    {
        _logger = logger;
        PutTheItemsSomewhere(emptyArrayFromWindsor);
    }

    public void DoSomething()
    {
        // Something that relies on the list of items...
    }
}

但正如这个例子所暗示的那样,由于某些原因,温莎打电话给错误的ctor而你却无法说服它(正如Krzysztof指出的那样)。然后你可以创建如下的装饰器类,其中只有一个构造函数,从而消除了歧义:

public class SpecificCtorServiceDecorator : IService
{
    private readonly IService _decorated;

    public SpecificCtorServiceDecorator(ILogger logger)
    {
        _decorated = new Service(logger);
    }

    public void DoSomething()
    {
        _decorated.DoSomething();
    }
}

然后您注册该课程:

container.Register(
    Component.For<IService>()
             .ImplementedBy<SpecificCtorServiceDecorator>());

当然最好不要让这个奇怪的默认值在其他构造函数中发生(search for "Poor-man's Dependency Injection"),但是在你不能控制类的情况下实际上想要(就像我在Linq2Sql中,或者如果它是对API的重大改变)那么这可能会让你摆脱困境。