如何获取Castle Windsor中某些依赖项的当前实例化实例列表?

时间:2015-03-24 18:01:34

标签: c# castle-windsor ioc-container

假设我有Bar的组件Foo,并在实现FooHappened接口的所有服务上通知该IFooConsumer方法。

我可以像这样写Bar

class Bar
{
    public Bar(IEnumerable<IFooConsumer> fooConsumers) { ... }

    public void Foo()
    {
        // foo-ing
        foreach (var f in _fooConsumers) f.FooHappened();
    }
}

它可以工作,但实例化Bar将实例化所有可能的IFooConsumer。如果我只需要通知IFooConsumer发生时存在的那些Foo,该怎么办?

有没有办法让某种跟踪器了解IFooConsumer的所有实例化实例?

我可以通过订阅IWindsorContainer.Kernel.ComponentCreated自己写一个,但是如果有这样的东西我感兴趣吗?或者可能有其他方法可以解决我的问题?

2 个答案:

答案 0 :(得分:2)

您可以创建一个简单的工具,如下图所示,每次组件实例化时都会进行事件注册。这里的代码是使用Winsor和Caliburn.Micro。这也将确保事件被取消注册,否则将导致奇怪的行为。在你的情况下,我不会让Bar直接激活所有类的事件,而是使用单例组件(如下面的IEventAggregator)将事件激发到多个类。这也将确保事件被取消注册,否则将导致奇怪的行为。在代码中,从IHandle派生的每个类都将接收事件。您可以根据需要进行更改。

如果您有任何疑问,请告诉我。

class EventRegistrationFacility : AbstractFacility
{
    private IEventAggregator _eventAggregator;
    protected override void Init()
    {
        Kernel.ComponentCreated += ComponentCreated;
        Kernel.ComponentDestroyed += ComponentDestroyed;
    }

    void ComponentCreated(Castle.Core.ComponentModel model, object instance)
    {
        if (!(instance is IHandle)) return;
        if (_eventAggregator == null) _eventAggregator = Kernel.Resolve<IEventAggregator>();
        _eventAggregator.Subscribe(instance);
    }

    void ComponentDestroyed(Castle.Core.ComponentModel model, object instance)
    {
        if (!(instance is IHandle)) return;
        if (_eventAggregator == null) return;
        _eventAggregator.Unsubscribe(instance);
    }

}

=== EDIT ====

将其与Sammy所描述的保镖结合起来:

public interface IBouncer {
    IEnumerable<IFooConsumer> WhoIsInside {get;}
    void WelcomeTo(IFooConsumer consumer);
    void EscortOut(IFooConsumer consumer);
}

public class Bouncer {
    private IList<IFooConsumer> _inside {get;}
    void WelcomeTo(IFooConsumer consumer) {
        _inside.Add(consumer);
}
    void EscortOut(IFooConsumer consumer);
        _inside.Remove(consumer);
    }
    IEnumerable<IFooConsumer> WhoIsInside {
        get {
            return _inside;
        }
    }



public Consumer: IFooConsumer {
    FooHappened() {
        // Do something.
}
    // no need to implement constructor/dispose
}


class Bar
{
    public Bar(IBouncer bouncer) { ... }

    public void Foo()
    {
        // foo-ing ==> alernatively create a function on Bouncer that does this. And keep WhoIsInside private.
        foreach (var f in bouncer.WhoIsInside) f.FooHappened();
    }
}


class BouncerRegistrationFacility : AbstractFacility
{
    private IBouncer _bouncer
    protected override void Init()
    {
        Kernel.ComponentCreated += ComponentCreated;
        Kernel.ComponentDestroyed += ComponentDestroyed;
    }

    void ComponentCreated(Castle.Core.ComponentModel model, object instance)
    {
        if (!(instance is IFooConsumer)) return;
        if (_bouncer == null) _bouncer = Kernel.Resolve<IEventAggregator>();
        _bouncer.WelcomeTo(instance);
    }

    void ComponentDestroyed(Castle.Core.ComponentModel model, object instance)
    {
        if (!(instance is IFooConsumer)) return;
        if (_bouncer == null) return;
        _bouncer.EscortOut(instance);
    }
}

虽然您需要更多代码来编写工具,但FooConsumers无需注册/取消注册。由于注册码最初必须写在所有FooConsumers中,因此它往往会重复。通过这种方式,订阅/取消订阅是作为佣金/退役要求完成的,只需要处理一次。

P.S。代码是用记事本编写的,可能包含编译错误。

答案 1 :(得分:1)

我认为,知道哪些物品在温莎城堡实例化的关键并不是最好的前进方式;您肯定需要访问一些容器方法,这样做会将您的组件链接到Castle,这不应该发生。

我建议的是创建一个组件IBouncer。该组件将在所有IFooConsumer中作为单例注入,在创建和处理时将其称为(置于一个选项,您可以使用其他方法)

public interface IBouncer {
     IEnumerable<IFooConsumer> WhoIsInside {get;}
     void WelcomeTo(IFooConsumer consumer);
     void EscortOut(IFooConsumer consumer);
}

public Consumer: IFooConsumer {
    public Consumer(IBouncer bouncer) {
        bouncer.WelcomeTo(this);
    }

    public Dispose() {
        bouncer.EscortOut(this); // dispose pattern ommitted
    }
}

现在,不要将IFooConsumer列表传递给Bar,而只需将IBouncer添加到其中,然后询问哪些消费者在里面。

class Bar
{
    public Bar(IBouncer bouncer) { ... }

    public void Foo()
    {
        // foo-ing
        foreach (var f in bouncer.WhoIsInside) f.FooHappened();
    }
}