事件聚合器 - Caliburn.Micro 文档系列

事件聚合器 (Event Aggregator)

Caliburn.Micro 预先捆绑了一个 Event Aggregator,方便地称为事件聚合器。对于那些不熟悉的人来说,事件聚合器是一种服务,它提供了以一种基于松散的方式将对象从一个实体发布到另一个实体的能力。事件聚合器实际上是一种模式,它的实现可以因框架而异。对于 Caliburn.Micro,我们专注于使我们的事件聚合器实现简单易用,而不牺牲特性和灵活性。

入门

如前所述,我们为你提供了一个事件聚合器的实现。这个实现实现了 IEventAggregator 接口,但是,如果需要,你可以提供自己的实现。请花点时间熟悉 IEventAggregator

public interface IEventAggregator {
	bool HandlerExistsFor(Type messageType);
	void Subscribe(object subscriber);
	void Unsubscribe(object subscriber);
	void Publish(object message, Action<Action> marshal);
}
创建与生命周期

要正确使用事件聚合器,它必须作为应用程序级别的服务存在。这通常通过将事件聚合器的实例创建为单例来实现。我们建议你使用依赖注入来获取对实例的引用,尽管我们并不强制这样做。下面的示例详细说明了如何创建事件聚合器实例,将其添加到 Caliburn.Micro 附带的 IoC 容器中 (尽管你可以随意使用任何容器) 并在视图模型中请求它。

// 将 EventAggregator 创建为单例
public class Bootstrapper : BootstrapperBase {
    private readonly SimpleContainer _container =
        new SimpleContainer();

     // ... Other Bootstrapper Config

    protected override void Configure(){
        _container.Singleton<IEventAggregator, EventAggregator>();
    }

    // ... Other Bootstrapper Config
}

// 在视图模型中获取 EventAggregator。
public class FooViewModel {
    private readonly IEventAggregator _eventAggregator;

    public FooViewModel(IEventAggregator eventAggregator) {
        _eventAggregator = eventAggregator;
    }
}

注意,我们在上面的代码中使用了 Bootstrapper,特别是 Configure 方法。不需要在特定位置连接事件聚合器,只需确保在首次请求之前创建它即可。

发布事件

一旦你获得了事件聚合器 实例的引用,你就可以开始发布事件了。我们称之为事件或消息以区分 .Net 事件,可以是你喜欢的任何对象。不需要以任何特定的方式构建事件。如下面的示例所示,Publish 方法可以接受从 System.Object 派生的任何实体并很乐意将它发布给任何感兴趣的订阅者。

public class FooViewModel {
    private readonly IEventAggregator _eventAggregator;

    public FooViewModel(IEventAggregator eventAggregator) {
        _eventAggregator = eventAggregator;

        _eventAggregator.PublishOnUIThread(new object());
        _eventAggregator.PublishOnUIThread("Hello World");
        _eventAggregator.PublishOnUIThread(22);
    }
}
使用自定义线程发布事件

按照约定,事件聚合器在 UI 线程上发布 (使用 PublishOnUIThread() 方法)。你可以在每次发布时重写此命令。请考虑下面的代码,它发布在后台线程上提供的消息。

_eventAggregator.Publish(new object(), action => {
    Task.Factory.StartNew(action);
});
订阅事件

任何实体都可以通过 Subscribe 方法将自身提供给事件聚合器来订阅任何事件。为了使这个功能易于使用,我们提供了一个特殊的接口 (IHandle),它将订阅者对给定类型的事件标记为感兴趣。

IHandle<TMessage> : IHandle {
    void Handle<TMessage>(TMessage message);
}

注意,通过实现上面的接口,你必须实现方法 Handle(T message)T 是你已指定自己感兴趣的消息类型。当发布匹配的事件类型时,将调用此方法。

public class FooViewModel : IHandle<object> {
    private readonly IEventAggregator _eventAggregator;

    public FooViewModel(IEventAggregator eventAggregator) {
        _eventAggregator = eventAggregator;
        _eventAggregator.Subscribe(this);
    }

    public void Handle(object message) {
        // Handle the message here.
    }
}
订阅许多事件

单个实体希望侦听多个事件类型的情况并不少见。由于我们使用了泛型,这就像向订阅者添加第二个 IHandle 接口一样简单。注意,Handle 方法现在使用新的事件类型重载。

public class FooViewModel : IHandle<string>, IHandle<bool> {
    private readonly IEventAggregator _eventAggregator;

    public FooViewModel(IEventAggregator eventAggregator) {
        _eventAggregator = eventAggregator;
        _eventAggregator.Subscribe(this);
    }

    public void Handle(string message) {
        // Handle the message here.
    }

    public void Handle(bool message) {
        // Handle the message here.
    }
}
多态的订阅者

Caliburn.Micro 的事件聚合器尊重多态性。在选择要调用的处理程序时,事件聚合器将从发送的事件中分配任何可分配事件类型的处理程序。这带来了很大的灵活性,并有助于重用。

public class FooViewModel : IHandle<object>, IHandle<string> {
    private readonly IEventAggregator _eventAggregator;

    public FooViewModel(IEventAggregator eventAggregator) {
        _eventAggregator = eventAggregator;
        _eventAggregator.Subscribe(this);
        _eventAggregator.PublishOnUIThread("Hello");
    }

    public void Handle(object message) {
        // This will be called
    }

    public void Handle(string message) {
        // This also
    }
}

在上面的示例中,因为 String 是从 System.Object 派生的,所以在发布 String 消息时将调用这两个处理程序。

查询处理程序

当订阅者被传递给事件聚合器时,它被分解为一个名为 Handler 的特殊对象,并保持弱引用。 我们提供了一种机制来查询事件聚合器以查看给定的事件类型是否有任何的处理程序,这在假设至少存在一个处理程序的特定场景中非常有用。

public class FooViewModel : IHandle<object> {
    private readonly IEventAggregator _eventAggregator;

    public FooViewModel(IEventAggregator eventAggregator) {
        _eventAggregator = eventAggregator;
        _eventAggregator.Subscribe(this);
    }

    public void Handle(object message){
        if (_eventAggregator.HandlerExistsFor(typeof(SpecialMessageEvent))){
            _eventAggregator.PublishOnUIThread(new SpecialEventMessage(message));
        }
    }
}
协同感知订阅者

如果你使用带有 Caliburn.Micro 的事件聚合器而不是通过 Nuget 使用它,则可以访问事件聚合器中的协同程序支持。 通过 IHandleWithCoroutine 接口支持协同程序。

public interface IHandleWithCoroutine<TMessage> : IHandle {
	IEnumerable<IResult> Handle(TMessage message);
}

下面的代码使用了事件聚合器的协同程序。在这个实例中,Activate 将异步启动,但 DoWork,将不会被调用,直到 Activate 完成。

public class FooViewModel : Screen, IHandleWithCoroutine<EventWithCoroutine> {
    private readonly IEventAggregator _eventAggregator;
    
    public FooViewModel(IEventAggregator eventAggregator) {
        _eventAggregator = eventAggregator;
        _eventAggregator.Subscribe(this);
    }
    
    public IEnumerable<IResult> Handle(EventWithCoroutine message) {
        yield return message.Activate();
        yield return message.DoWork();
    }
}
public class EventWithCoroutine {
    public IResult Activate() {
        return new TaskResult(Task.Factory.StartNew(() => {
                // Activate logic
            }));
    }

    public IResult DoWork() {
        return new TaskResult(Task.Factory.StartNew(() => {
            // Do work logic
        }));
    }
}
任务感知订阅者

Caliburn.Micro 还为基于任务的订阅者提供支持,在这些订阅者中,协同程序的异步功能是需要的,但是以一种更轻量级的方式。要使用此功能,请实现 IHandleWithTask 接口,如下所示:

public interface IHandleWithTask<TMessage> : IHandle {
    Task Handle(TMessage message);
}

实现上述接口的任何订阅者都可以以基于任务的方式处理事件。

public class FooViewModel : Screen, IHandleWithTask<object> {
    private readonly IEventAggregator _eventAggregator;

    public FooViewModel(IEventAggregator eventAggregator) {
        _eventAggregator = eventAggregator;
        _eventAggregator.Subscribe(this);
    }

    public Task Handle(object message) {
        return Task.Factory.StartNew(() => message);
    }
}
取消订阅和泄漏

标准 .Net 事件的问题在于它们容易发生内存泄漏。 我们通过维持对订阅者的弱引用来避免这种情况。如果引用订阅者的唯一对象是事件聚合器,那么就允许它超出范围并最终被垃圾收集。但是,我们仍然提供了一种明确的方式来取消订阅,以允许条件处理,如下所示:

public class FooViewModel : Screen, IHandle<object> {
    private readonly IEventAggregator _eventAggregator;

    public FooViewModel(IEventAggregator eventAggregator) {
        _eventAggregator = eventAggregator;
        _eventAggregator.Subscribe(this);
    }

    public void Handle(object message) {
        // Handle the message here.
    }

    protected override void OnActivate() {
        _eventAggregator.Subscribe(this);
        base.OnActivate();
    }

    protected override void OnDeactivate(bool close) {
        _eventAggregator.Unsubscribe(this);
        base.OnDeactivate(close);
    }
}

在上面的代码中,屏幕用于在视图模型上公开生命周期事件。 有关此内容的更多信息,请参阅维基上的屏幕、导体和组合文章。

自定义处理结果

在更复杂的场景中,可能需要覆盖具有结果的处理程序的默认处理。 在这种情况下,你可以用自己的实现替换现有的实现。 首先,我们创建一个新的处理程序类

IHandleAndReturnString<T> : IHandle {
    string Handle<T>(T message);
}

接下来我们创建新的结果处理器。 这可以在引导程序中配置。

var standardResultProcesser = EventAggregator.HandlerResultProcessing;
    EventAggregator.HandlerResultProcessing = (target, result) =>
    {
        var stringResult = result as string;
        if (stringResult != null)
            MessageBox.Show(stringResult);
        else
            standardResultProcesser(target, result);
    };

现在,每当处理一个事件返回一个字符串时,它都会被捕获并显示在一个 MessageBox 中。如果结果不能从 string 中分配,则新处理程序将回退到默认实现。 需要特别注意的是,这个特性并不是为请求/响应的使用而设计的,因此对其进行处理肯定会在发布时产生瓶颈。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值