温莎城堡:强制解析器使用指定的构造函数

本文关键字:构造函数 城堡 | 更新日期: 2023-09-27 18:12:33

示例如下:

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有两个重载的构造函数。当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>())));

它是有效的,但也许温莎城堡提供了更好的方法?

任何帮助都非常感谢。

谢谢。

温莎城堡:强制解析器使用指定的构造函数

Windsor不支持这种场景,因为它打破了它(和大多数容器)基于的一个不成文的假设:"所有构造函数都是平等的"。

这意味着,无论它选择哪个构造函数,组件的行为都不应该有功能上的差异。在所有条件相同的情况下,组件拥有的依赖越多,它拥有的功能就越多,这就是为什么Windsor会首先选择更贪婪的构造函数,但在像你这样的情况下,我会说两种情况中的一种:

  • 你的组件实际上可能是伪装成一个组件的两个组件。在这种情况下,你可能会想要拆分它。
  • 你的组件实际上使用它所拥有的两个依赖项来运行,因此它应该有一个单独的构造函数来接受它们。

我见过的另一个场景是这样的:

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

虽然乍一看这似乎是一个聪明的想法,但充其量它是多余的,令人困惑的和不必要的。如果您的情况与此类似,我将删除第二个构造函数。

嗯,这是可怕的,但有一种方法(我不得不这样做与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...
    }
}

但正如这个例子所表明的那样,由于某种原因,温莎打错了医生,你无法说服它(正如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>());

当然最好不要有这种奇怪的默认值在其他构造函数的事情(搜索"穷人的依赖注入"),但在你不控制你真正想要的类的情况下(像我在Linq2Sql,或者如果它将是一个突破性的改变一个API),那么这可能会让你摆脱麻烦。