5

設計の質問–多形イベント処理

現在、現在のプロジェクトでイベントハンドルの数を減らしようとしています。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);
    }
}

そして、あいまいさの理由から、これを行うことはできません。だから私の質問は、この問題へのより良いアプローチは何でしょうか?

4

5 に答える 5

3

コーディングを抽象化/簡素化するためにイベントハンドルの数を減らしたい場合は、ダブルディスパッチデザインパターンをイベント引数に適用するのが最適です。これは基本的に、安全な型キャストを実行する必要があるためのエレガントな(しかし言葉の多い)修正です(/はinstanceofチェックです)

于 2009-07-29T10:21:04.563 に答える
1

ポリモーフィズムから抜け出し、間接参照の使用を検討します。特に、まだ行っていない場合は、イベントアグリゲーターパターンを検討します。Fowlerは最初に@http ://martinfowler.com/eaaDev/EventAggregator.htmlで、さらにアイデアが必要な場合はJeremyMillerによる投稿を行います。

乾杯、
ベリール

于 2009-07-27T00:18:50.213 に答える
1

いくつかのオプションを検討できます(ここで何を達成したいのか正確にはわかりません):

1. EventArgs の階層を作成し、オブザーバーが関心のあるものをフィルタリングする責任を負うようにします (これは、回答で提案したものです)。これは、一部のオブザーバーが複数のタイプのメッセージ (理想的には基本クラスのタイプによって記述される) に関心がある場合に特に意味があります。

2. .Net デリゲートを使用しないでください。デリゲートを登録するときに、期待されるタイプのイベントも受け取るように、自分で実装してください。これは、(1) の作業を完了したことを前提としていますが、フィルタリングをオブザーバーではなくクラスに渡したいと考えています。

例 (未テスト):

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 で行われる方法でイベントを実装し、これまでに公開されたイベントの基になるイベントを持たないようにします。オブザーバーよりも多くのイベントが予想される場合、これは理にかなっています。

例えば:

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

回答を詳しく知りたい場合はお知らせください。

于 2009-07-28T10:07:27.350 に答える
0

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を再利用するだけです(ただし、キーは異なります)。

于 2009-07-29T10:13:33.473 に答える