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

发布于 2024-12-12 17:13:41 字数 1109 浏览 0 评论 0原文

示例如下:

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

它有效,但温莎城堡可能提供了一些更好的方法来做到这一点?

非常感谢任何帮助。

谢谢。

Here is the example:

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

All these components are registered in Castle Windsor container.

But class ComponentC has 2 overloaded constructors. Any of them can be used when ComponentC is being activated.

I need ComponentC(IComponentB b) constructor to be used.

For a moment I'm using UsingFactoryMethod() method to resolve that:

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

It works, but probably Castle Windsor provides some better way to do that?

Any help is really appreciated.

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

心安伴我暖 2024-12-19 17:13:41

Windsor 不提供对这种情况的支持,因为它打破了它(以及大多数容器)运行所基于的不成文假设之一:“所有构造函数都是平等创建的”。

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

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

我见过的另一种情况是这样的:

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

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

Windsor doesn't provide support for this scenario, because it breaks one of the unwritten assumptions it (and most containers) operates based on: "all constructors are created equal".

What that means, is that regardless of which constructor it choses there should be no functional differences in the behaviour of the component. All things being equal the more dependencies a component has the more capabilities it has, that's why Windsor will tend to pick greedier constructors first, but in case like yours I'd say either of two things are happening:

  • your component may actually be two components disguised as one. In that case you probably will want to split it.
  • your component actually does operate with both dependencies it has, and therefore it should have a single constructor that takes them both.

Another scenario I've seen is something like that:

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

While this might seem like a clever idea at first, at best it's superfluous, confusing and unnecessary. If your case looks like this one, I'd just remove the second constructor.

攒眉千度 2024-12-19 17:13:41

嗯,这很糟糕,但有一种方法(过去我不得不使用 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...
    }
}

但正如示例所示,由于某种原因,Windsor 调用了错误的 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>());

当然,最好不要使用这种奇怪的默认值-在其他构造函数中发生的事情(搜索对于“穷人的依赖注入”),但在您无法控制您真正想要的类的情况下(就像我在 Linq2Sql 中一样,或者如果它这将是对 API 的重大更改),那么这可能会让您摆脱麻烦。

Umm, it's awful but there is one way (I've had to do this with Linq2Sql DataContext objects in the past). You create a decorator class and register that instead.

Let's say you have this interface:

public interface IService 
{
    void DoSomething();
}

And you have an implementation as follows:

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...
    }
}

But as the example suggests, for some reason Windsor is calling the wrong ctor and you can't convince it otherwise (as Krzysztof correctly points out). Then you could create the decorator class as follows, which only has one constructor and thus removes the ambiguity:

public class SpecificCtorServiceDecorator : IService
{
    private readonly IService _decorated;

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

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

You'd then register that class instead:

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

Of course better would be to not have this weird default-values-in-other-constructors thing going on (search for "Poor-man's Dependency Injection"), but in the case where you're not in control of the class you actually want (like I was in Linq2Sql, or if it would be a breaking change to an API) then this might get you out of trouble.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文