使用以下方式实现双重调度dynamic
:
public interface IDomainEvent {}
public class DomainEventDispatcher
{
private readonly List<Delegate> subscribers = new List<Delegate>();
public void Subscribe<TEvent>(Action<TEvent> subscriber) where TEvent : IDomainEvent
{
subscribers.Add(subscriber);
}
public void Publish<TEvent>(TEvent domainEvent) where TEvent : IDomainEvent
{
foreach (Action<TEvent> subscriber in subscribers.OfType<Action<TEvent>>())
{
subscriber(domainEvent);
}
}
public void PublishQueue(IEnumerable<IDomainEvent> domainEvents)
{
foreach (IDomainEvent domainEvent in domainEvents)
{
// Force double dispatch - bind to runtime type.
Publish(domainEvent as dynamic);
}
}
}
public class ProcessCompleted : IDomainEvent { public string Name { get; set; } }
在大多数情况下有效:
var dispatcher = new DomainEventDispatcher();
dispatcher.Subscribe((ProcessCompleted e) => Console.WriteLine("Completed " + e.Name));
dispatcher.PublishQueue(new [] { new ProcessCompleted { Name = "one" },
new ProcessCompleted { Name = "two" } });
完成一项
完成了两个
但是如果子类对调度代码不可见,则会导致运行时错误:
public static class Bomb
{
public static void Subscribe(DomainEventDispatcher dispatcher)
{
dispatcher.Subscribe((Exploded e) => Console.WriteLine("Bomb exploded"));
}
public static IDomainEvent GetEvent()
{
return new Exploded();
}
private class Exploded : IDomainEvent {}
}
// ...
Bomb.Subscribe(dispatcher); // no error here
// elsewhere, much later...
dispatcher.PublishQueue(new [] { Bomb.GetEvent() }); // exception
RuntimeBinderException
类型“object”不能用作泛型类型或方法“DomainEventDispatcher.Publish(TEvent)”中的类型参数“TEvent”
这是一个人为的例子;一个更现实的事件是另一个集会内部的事件。
如何防止此运行时异常?如果这不可行,我怎样才能在Subscribe
方法中检测到这种情况并快速失败?
编辑:消除动态转换的解决方案是可以接受的,只要它们不需要知道所有子类的访问者样式类。