设计问题 - 多态事件处理
我目前正在尝试减少当前项目中的事件句柄数量。我们有多个通过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);
}
}
出于含糊不清的原因,我们不能这样做。所以我的问题是如何更好地解决这个问题?
答案 0 :(得分:3)
如果您正在尝试减少事件句柄的数量,以便抽象/简化您必须执行的编码,那么将Double Dispatch设计模式应用于您的事件args将是完美的。对于必须执行安全类型转换(/是instanceof检查)来说,这基本上是一个优雅(但有韵味)的修复
答案 1 :(得分:1)
我可以使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 }));
}
答案 2 :(得分:1)
从多态性中休息一下,看看使用间接,特别是Event Aggregator模式(如果你还没有); Fowler首先@ http://martinfowler.com/eaaDev/EventAggregator.html然后由Jeremy Miller发布,如果你需要更多的想法。
干杯,
Berryl
答案 3 :(得分:1)
你可以考虑几个选项(我不确定你想在这里实现什么):
1。创建EventArgs的层次结构,并让观察者负责过滤他们感兴趣的内容(这是您在答案中提出的内容)。如果某些观察者对多种类型的消息感兴趣(理想情况下由基类类型描述),这尤其有意义。
2。不要使用.Net委托,只需自己实现,这样当您注册委托时,它也会采用它所期望的事件类型。这假设您已经完成了(1)中的工作,但是您希望将过滤传递给您的类而不是观察者
E.g。 (另):
enum MessageType
{
Type1,Type2
}
private Dictionary<MessageType, TestHandlerWithInheritance> 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中的方式实现事件,这样就不会有暴露事件的基础事件。如果您希望事件多于观察者,那么这是有道理的。
E.g:
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;
}
}
如果您希望我扩展任何答案,请告诉我。
答案 4 :(得分:0)
如果您正在尝试减少保存RAM的事件句柄数,请执行microsoft所做的操作(在System.ComponentModel.Component中)并使用EventHandlerList来跟踪所有事件。 Here is an article that describes conserving memory use with an EventHandlerList和here is a similar article that's written in 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(但使用不同的键)。