简体   繁体   English

设计帮助–多态事件处理

[英]Design Help – Polymorphic Event Handling

Design Question – Polymorphic Event Handling 设计问题–多态事件处理

I'm currently trying to reduce the number of Event Handles in my current project. 我目前正在尝试减少当前项目中的事件句柄数。 We have multiple systems that send data over USB. 我们有多个通过USB发送数据的系统。 I currently have a routine to read in the messages and parse the initial header details to determine which system the message came from. 我目前有一个例程可以读取消息并解析初始标头详细信息,以确定消息来自哪个系统。 The headers are a little different, so the EventArgs I created are not the same. 标头略有不同,因此我创建的EventArgs不同。 Then I notify all “observers” of the change. 然后,我将更改通知所有“观察员”。 So what I have right now is the following: 所以我现在有以下内容:

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 code 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;
    }

And in the parse algorithm I have something like this based on which message it is: 在解析算法中,基于它是哪条消息,我有类似的内容:

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

What I really want to do is this: 我真正想做的是:

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);
    }
}

And for ambiguity reasons we can't do this. 而且出于歧义的原因,我们不能这样做。 So my question is what would be a better approach to this problem? 所以我的问题是解决这个问题的更好方法是什么?

If you're trying to reduce the number of event handles in order abstract / simplify the coding you have to do, then applying the Double Dispatch design pattern to your event args would be perfect. 如果您要减少事件句柄的数量以简化抽象/简化编码,那么将Double Dispatch设计模式应用于事件args将是完美的选择。 It's basically an elegant (but wordy) fix for having to perform safe type casts (/ is instanceof checks) 对于必须执行安全的类型强制转换(/是instanceof检查),这基本上是一个优雅的(但冗长的)修复程序

I could make MyEvent1Args and MyEvent2Args derive from a common base class and do the following: 我可以使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;
        }
    }

And in the parse algorithm I have something like this based on which message it is: 在解析算法中,基于它是哪条消息,我有类似的内容:

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

Take a break from polymorphism and look into using indirection, specifically the Event Aggregator pattern if you haven't already; 中断多态,研究使用间接寻址,特别是使用Event Aggregator模式(如果还没有的话); Fowler first @ http://martinfowler.com/eaaDev/EventAggregator.html and then postings by Jeremy Miller if you need more ideas. Fowler首先@ @ http://martinfowler.com/eaaDev/EventAggregator.html ,然后如果需要更多创意,请Jeremy Miller发表。

Cheers, 干杯,
Berryl 贝里

You could consider a few options (I am not sure what exactly you want to achieve here): 您可以考虑以下几种选择(我不确定您到底想在这里实现什么):

1. Create a hierarchy of EventArgs and make the observers responsible for filtering stuff they are interested in (this is what you proposed in your answer). 1.创建一个EventArgs层次结构,并使观察员负责过滤他们感兴趣的内容(这就是您在回答中所建议的)。 This especially makes sense if some observers are interested in multiple types of messages, ideally described by the base class type. 如果某些观察者对多种消息感兴趣,这尤其有意义,最好用基类类型来描述。

2. Don't use .Net delegates, just implement it yourself such that when you register the delegate it also takes the type of event it expects. 2.不要使用.Net委托,只需自己实现即可,这样当您注册委托时,它也将获取预期的事件类型。 This assumes you have done the work from (1), but you want to pass the filtering to your class and not observers 假设您已经完成了(1)的工作,但是您希望将过滤传递给您的类而不是观察者

Eg (untested): 例如(未试用):

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);
  }
}

And when a new message arrives, you run the correct delegate from the handlers dictionary. 当收到新消息时,您可以从处理程序字典中运行正确的委托。

3. Implement events in the way its done in WinForms, so that you don't have an underlying event for ever exposed event. 3.以在WinForms中完成事件的方式来实现事件,以使您没有暴露事件的基础事件。 This makes sense if you expect to have more events than observers. 如果您希望事件多于观察者,那么这是有道理的。

Eg: 例如:

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;
    }
}

Please let me know if you want me to expand on any of the answers. 如果您希望我提供更多答案,请告诉我。

If you're trying to reduce the number of event handles to save RAM, do what microsoft do (in System.ComponentModel.Component) and use an EventHandlerList to track all of your events. 如果要减少事件句柄的数量以节省RAM,请执行Microsoft所做的操作(在System.ComponentModel.Component中),并使用EventHandlerList跟踪所有事件。 Here is an article that describes conserving memory use with an EventHandlerList , and here is a similar article that's written in C#. 这是一篇介绍EventHandlerList的节省内存使用的 文章,这是用C#编写的类似文章。 .

The gist of it is that you can declare a single EventHandlerList (remember to dispose it) in your class, along with a unique key: 要点是,您可以在类中声明一个EventHandlerList(记住要将其处置),以及一个唯一的键:

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

...override the event property: ...覆盖事件属性:

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

...and provide a RaiseEvent method: ...并提供RaiseEvent方法:

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

Of course, you just reuse the same EventHandlerList for all your events (but with different keys). 当然,您只需对所有事件重用相同的EventHandlerList(但使用不同的键)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM