假设我有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
自己写一个,但是如果有这样的东西我感兴趣吗?或者可能有其他方法可以解决我的问题?
答案 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();
}
}