を使用して二重ディスパッチを実装する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" } });
1つ完成
2つ完成
ただし、サブクラスがディスパッチ コードから見えない場合は、実行時エラーが発生します。
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
型「オブジェクト」は、ジェネリック型またはメソッド「DomainEventDispatcher.Publish(TEvent)」の型パラメーター「TEvent」として使用できません
これは不自然な例です。より現実的なものは、別のアセンブリの内部にあるイベントです。
この実行時例外を防ぐにはどうすればよいですか? それが不可能な場合、Subscribe
メソッドでこのケースを検出してすばやく失敗するにはどうすればよいですか?
編集:動的キャストを排除するソリューションは、すべてのサブクラスを認識しているビジター スタイルのクラスを必要としない限り、許容されます。