若要通过发布服务简化事件的触发过程,FireEvent()方法需要接受多个参数,并将它们传递给订阅者。而且,发布服务的调用者并不提供订阅者调用的操作名。要实现这一目的,FireEvent()方法会访问它的堆栈帧,以获取它正在调用的方法名。然后使用重载版本的FireEvent(),它会接收方法名。该方法紧接着会调用辅助方法PublishPersistent(),发布给所有的持久订阅者,以及调用PublishTransient()辅助方法,发布给所有的临时订阅者。两种发布方法的实现几乎完全相同:它们通过访问SubscriptionManager<T>以获取各自的订阅者列表,然后使用Publish()方法触发事件。订阅者以订阅者代理数组的形式返回。该数据会被传递给Publish()方法。
采用这种办法,Publish()可以简单地调用订阅者。但是,我还需要支持事件的并发发布,如果能够这样,即使订阅者在处理事件时耗费时间过长,仍然不会影响其它订阅者能够及时地接收事件。注意,将事件操作标记为单向并不能保证异步调用。此外,在事件操作没有被标记为单向操作时,我还需要支持并发发布。Publish()定义了两个匿名方法。第一个匿名方法调用了Invoke()辅助方法,通过Invoke()方法触发事件,并传递到提供的单独的订阅者。如果SubscriptionManager<T>指定要求关闭代理,则匿名方法还会关闭代理。由于Invoke()方法不可能调用编译后的指定的订阅者类型,因此需要使用反射对调用进行迟绑定。同时,Invoke()方法还会禁止调用抛出的任何异常,因为这些都不是发布方所需要关注的。第二个匿名方法则对第一个匿名方法委托对象进行排队,以便于线程池中的线程执行。最后,Publish()对方法提供的subscribers数组中的每个订阅者执行第二个匿名方法的操作。
注意,PublishService<T>对待订阅者是一视同仁的,它并没有区分订阅者是临时的,还是持久的。唯一的区别是在发布到持久订阅者之后,需要关闭代理。当然不一致的地方还包括获取订阅列表的方法,分别为GetTransientList()和GetPersistentList()。两者之中,GetTransientList()更简单:
public abstract class SubscriptionManager<T> where T : class
{
internal static T[] GetTransientList(string eventOperation)
{
lock(typeof(SubscriptionManager<T>))
{
if(m_TransientStore.ContainsKey(eventOperation))
{
List<T> list = m_TransientStore[eventOperation];
return list.ToArray( );
}
return new T[]{};
}
}
//更多成员
}