设计帮助 – 多态事件处理

设计问题 – 多态事件处理

我目前正在尝试减少当前项目中的事件句柄数量。 我们有多个通过USB发送数据的系统。 我目前有一个例程来读取消息并解析初始标头详细信息以确定消息来自哪个系统。 标题有点不同,所以我创建的EventArgs不一样。 然后我通知所有“观察员”这一变化。 所以我现在所拥有的是以下内容:

public enum Sub1Enums : byte { ID1 = 0x01, ID2 = 0x02 } public enum Sub2Enums : ushort { ID1 = 0xFFFE, ID2 = 0xFFFF } public class MyEvent1Args { public Sub1Enums MessageID; public byte[] Data; public MyEvent1Args(Sub1Enums sub1Enum, byte[] data) { MessageID = sub1Enum; Data = data; } } public class MyEvent2Args { public Sub2Enums MessageID; public byte[] Data; public MyEvent2Args(Sub2Enums sub2Enum, byte[] data) { MessageID = sub2Enum; Data = data; } } 

Form1代码

 public class Form1 { public delegate void TestHandlerCurrentlyDoing(MyEvent1Args eventArgs1); public delegate void TestHandlerCurrentlyDoingAlso(MyEvent2Args eventArgs2); public event TestHandlerCurrentlyDoing mEventArgs1; public event TestHandlerCurrentlyDoingAlso mEventArgs2; public Form1() { mEventArgs1 += new TestHandlerCurrentlyDoing(Form1_mEventArgs1); mEventArgs2 += new TestHandlerCurrentlyDoingAlso(Form1_mEventArgs2); } void Form1_mEventArgs2(MyEvent2Args eventArgs2) { // Do stuff here Sub2Enums mid = my_event2_args.MessageID; byte[] data = my_event2_args.Data; } void Form1_mEventArgs1(MyEvent1Args eventArgs1) { // Do stuff here Sub1Enums mid = my_event1_args.MessageID; byte[] data = my_event1_args.Data; } 

在解析算法中,我根据它的消息得到类似的东西:

 void ParseStuff() { if (mEventArgs1 != null) { mEventArgs1(new MyEvent1Args(Sub1Enums.ID1, new byte[] { 0x01 })); } if (mEventArgs2 != null) { mEventArgs2(new MyEvent2Args(Sub2Enums.ID2, new byte[] { 0x02 })); } } 

我真正想做的是:

 public class Form1 { public delegate void TestHandlerDesired(MyEvent1Args eventArgs1); public delegate void TestHandlerDesired(MyEvent2Args eventArgs2); public event TestHandlerDesired mEventArgs; public Form1() { mEventArgs += new TestHandlerDesired (Form1_mEventArgs1); mEventArgs += new TestHandlerDesired (Form1_mEventArgs2); } } 

出于含糊不清的原因,我们不能这样做。 所以我的问题是如何更好地解决这个问题呢?

如果您正在尝试减少事件句柄的数量,以便抽象/简化您必须执行的编码,那么将Double Dispatch设计模式应用于您的事件args将是完美的。 对于必须执行安全类型转换(/是instanceof检查),它基本上是一个优雅(但是冗长)的修复

我可以使MyEvent1Args和MyEvent2Args派生自一个公共基类并执行以下操作:

 public class BaseEventArgs : EventArgs { public byte[] Data; } public class MyEvent1Args : BaseEventArgs { … } public class MyEvent2Args : BaseEventArgs { … } public delegate void TestHandlerWithInheritance(BaseEventArgs baseEventArgs); public event TestHandlerWithInheritance mTestHandler; mTestHandler += new TestHandlerWithInheritance(TestHandlerForEvent1Args); mTestHandler += new TestHandlerWithInheritance(TestHandlerForEvent2Args); void TestHandlerForEvent1Args(BaseEventArgs baseEventArgs) { MyEvent1Args my_event1_args = (baseEventArgs as MyEvent1Args); if (my_event1_args != null) { // Do stuff here Sub1Enums mid = my_event1_args.MessageID; byte[] data = my_event1_args.Data; } } void TestHandlerForEvent2Args(BaseEventArgs baseEventArgs) { MyEvent2Args my_event2_args = (baseEventArgs as MyEvent2Args); if (my_event2_args != null) { // Do stuff here Sub2Enums mid = my_event2_args.MessageID; byte[] data = my_event2_args.Data; } } 

在解析算法中,我根据它的消息得到类似的东西:

  if (mTestHandler!= null) { mTestHandler (new MyEvent1Args(Sub1Enums.ID1, new byte[] { 0x01 })); } if (mTestHandler!= null) { mTestHandler (new MyEvent2Args(Sub2Enums.ID2, new byte[] { 0x02 })); } 

从多态性中rest一下,看看使用间接,特别是Event Aggregator模式(如果你还没有); Fowler首先@ http://martinfowler.com/eaaDev/EventAggregator.html然后由Jeremy Miller发布,如果你需要更多的想法。

干杯,
Berryl

你可以考虑几个选项(我不确定你想在这里实现什么):

1.创建EventArgs的层次结构,并让观察者负责过滤他们感兴趣的东西(这是你在答案中提出的)。 如果某些观察者对多种类型的消息感兴趣(理想情况下由基类类型描述),这尤其有意义。

2.不要使用.Net委托,只需自己实现,这样当你注册委托时,它也会采用它所期望的事件类型。 这假定您已完成(1)中的工作,但您希望将过滤传递给您的类而不是观察者

例如(未经测试):

 enum MessageType { Type1,Type2 } private Dictionary handlers; public void RegisterObserver(MessageType type, TestHandlerWithInheritance handler) { if(!handlers.ContainsKey(type)) { handlers[key] = handler; } else { handlers[key] = Delegate.Combine(handlers[key] , handler); } } 

当一条新消息到达时,您将从处理程序字典中运行正确的委托。

3.以在WinForms中完成的方式实现事件,这样您就不会有暴露事件的基础事件。 如果您希望有比观察者更多的事件,这是有道理的。

例如:

 public event EventHandler SthEvent { add { base.Events.AddHandler(EVENT_STH, value); } remove { base.Events.RemoveHandler(EVENT_STH, value); } } public void AddHandler(object key, Delegate value) { ListEntry entry = this.Find(key); if (entry != null) { entry.handler = Delegate.Combine(entry.handler, value); } else { this.head = new ListEntry(key, value, this.head); } } public void RemoveHandler(object key, Delegate value) { ListEntry entry = this.Find(key); if (entry != null) { entry.handler = Delegate.Remove(entry.handler, value); } } private ListEntry Find(object key) { ListEntry head = this.head; while (head != null) { if (head.key == key) { return head; } head = head.next; } return head; } private sealed class ListEntry { // Fields internal Delegate handler; internal object key; internal EventHandlerList.ListEntry next; // Methods public ListEntry(object key, Delegate handler, EventHandlerList.ListEntry next) { this.next = next; this.key = key; this.handler = handler; } } 

如果您希望我扩展任何答案,请告诉我。

如果您正在尝试减少保存RAM的事件句柄数,请执行microsoft所做的操作(在System.ComponentModel.Component中)并使用EventHandlerList跟踪所有事件。 这篇文章描述了使用EventHandlerList保存内存使用 , 这里有一篇用C#编写的类似文章。 。

它的要点是你可以在你的类中声明一个EventHandlerList(记得处理它)以及一个唯一的键:

 public class Foo { protected EventHandlerList listEventDelegates = new EventHandlerList(); static readonly object mouseDownEventKey = new object(); 

…覆盖事件属性:

 public event MouseEventHandler MouseDown { add { listEventDelegates.AddHandler(mouseDownEventKey, value); } remove { listEventDelegates.RemoveHandler(mouseDownEventKey, value); } } 

…并提供RaiseEvent方法:

 protected void RaiseMouseDownEvent(MouseEventArgs e) { MouseEventHandler handler = (MouseEventHandler) base.Events[mouseDownEventKey]; if (handler != null) { handler(this, e); } } 

当然,您只需为所有事件重复使用相同的EventHandlerList(但使用不同的键)。