簡體   English   中英

如何通過另一個“聚合對象”在一組相同對象(同一類-不同實例)之間聚合事件?

[英]How to aggregate events across a collection of identical objects (same class - different instances) through another 'aggregation object'?

我努力為這個問題寫一個更合適的“標題”-當我對解決方案和正確的術語有了更好的理解后,我將嘗試並清理它。 :D

在我從事的最后兩個項目中,有一種模式在重復(這些項目都是高度事件驅動的)。

模式是:

public interface ISourceOfEvents {
    event Action<EventInfo1> SomethingHappened1;
    event Action<EventInfo2> SomethingHappened2;
    {...}
}

public class SingleSourceOfEvents : ISourceOfEvents {
     public event Action<EventInfo1> SomethingHappened1 = delegate { };
     public event Action<EventInfo2> SomethingHappened2 = delegate { };
     {...}
}

public class GroupSourceOfEvents : ISourceOfEvents {

    public IList<ISourceOfEvents> SourceOfEventsGroup { get; private set; }

    public event Action<EventInfo1> SomethingHappened1 = delegate { };
    public event Action<EventInfo2> SomethingHappened2 = delegate { };
    {...}

    public GroupSourceOfEvents(IList<ISourceOfEvents> sourceOfEventsGroup) {

        SourceOfEventsGroup = sourceOfEventsGroup;

        foreach (var s in SourceOfEventsGroup) {

            s.SomethingHappened1 += x => SomethingHappened1(x);
            s.SomethingHappened2 += x => SomethingHappened2(x);
            {...}
        }
    }
}

我的問題歸結為“如何使GroupSourceOfEvents的構造函數自動映射事件綁定?”。

當前,代碼難以維護,因為項目中有多個地方需要添加新事件。 ISourceOfEvents接口使我可以將事件添加到中心位置,並在需要添加事件的任何地方獲得編譯器錯誤,但是對於GroupSourceOfEvents的事件綁定器映射或使其完全動態,我需要一個類似的解決方案。

我想要類似的東西:

// PSEUDO-CODE WARNING! :D
// PSEUDO-CODE WARNING! :D
// PSEUDO-CODE WARNING! :D

public GroupSourceOfEvents(IList<ISourceOfEvents> sourceOfEventsGroup) {

    SourceOfEventsGroup = sourceOfEventsGroup;

    var events = typeof(ISourceOfEvents).GetEvents();

    foreach (var s in SourceOfEventsGroup) {

        foreach (var e in events) {
            e.AddEventHandler(s, e.GetRaiseMethod(this));
        }
    }
}

我一半的問題是將參數映射在一起。 在當前工作的代碼(頂部示例)中,使用了lambda表達式,但是我不確定使用偽代碼(底部示例)中使用的“技術風格”如何實現。 我的問題的另一半是GetRaiseMethod(this)返回的是與delegate相對的MethodInfo (這是我需要使用AddEventHandler所需要的),並且我不確定如何“到達” delegate

任何幫助將不勝感激。 我也願意接受可能更標准的不同模式或方法(如果不是這樣的話)。

我正在使用C#和.Net 4。

我不確定我是否完全了解您要做什么。 據我了解,您正在嘗試創建一個包含ISourceOfEvents對象列表的“聚合器”對象( GroupSourceOfEvents )。

然后,稍后,您要允許訂閱者訂閱GroupSourceOfEvents對象公開的事件。 每當包含的ISourceOfEvents對象中的任何一個引發事件時,都會引發這些事件。

我認為以下兩種解決方案可以滿足您的需求。 這些解決方案使用事件訪問器(添加/刪除)。 第一個是一個帶有許多陷阱的簡單示例。 第二個是更復雜的示例,我認為它解決了第一個示例的所有陷阱。

public class GroupSourceOfEvents : ISourceOfEvents
{
    public IList<ISourceOfEvents> SourceOfEventsGroup { get; private set; }

    public event Action<EventInfo1> SomethingHappened1
    {
        add
        {
            foreach (var s in SourceOfEventsGroup)
            {
                s.SomethingHappened1 += value;
            }
        }
        remove
        {
            foreach (var s in SourceOfEventsGroup)
            {
                s.SomethingHappened1 -= value;
            }
        }
    }

    public event Action<EventInfo2> SomethingHappened2
    {
        add
        {
            foreach (var s in SourceOfEventsGroup)
            {
                s.SomethingHappened2 += value;
            }
        }
        remove
        {
            foreach (var s in SourceOfEventsGroup)
            {
                s.SomethingHappened2 -= value;
            }
        }
    }
    // {...}

    public GroupSourceOfEvents(IList<ISourceOfEvents> sourceOfEventsGroup)
    {
        SourceOfEventsGroup = sourceOfEventsGroup;

        foreach (var s in SourceOfEventsGroup)
        {
            // {...}
        }
    }
}

我能想到的主要陷阱是,在任何訂閱者開始訂閱任何事件之前,您必須確保將所有ISourceOfEvents對象都添加到GroupSourceOfEvents對象中。 這是因為,如果您從列表中添加任何ISourceOfEvents對象,則任何先前訂閱的偵聽器都不會收到有關新添加的對象的事件的通知。 同樣,如果在從列表中刪除了任何ISourceOfEvents對象之后,任何訂閱者都取消了對GroupSourceOfEvents事件的訂閱,則這些被刪除對象上的事件也將無法正確取消訂閱。 另一個問題是事件的添加/刪除訪問器內沒有鎖定,如果涉及多線程,則可能是一個問題。

您可以使用相同方法的更復雜版本來解決這些陷阱。 您可以擁有一個與每個事件相對應的私有委托變量,並且在每個事件的添加/刪除代碼中,您可以使用局部變量來存儲所有訂閱者。 然后,您必須避免公開公開SourceOfEventsGroup列表,並提供一些方法從列表中添加/刪除,以便添加后可以使用私有委托變量來訂閱新對象的事件,或者刪除后可以使用私有委托變量以退訂已刪除對象的事件。

下面是上述示例的另一個版本,試圖解決我提到的問題。 請注意,我已將列表公開為IEnumerable<ISourceOfEvents>以避免公開公開列表的添加/刪除功能。 我添加了AddSource方法,以允許添加源並使用已注冊的訂閱者訂閱該源的事件。 同樣,有一個RemoveSource方法可以處理從列表中刪除源並取消訂閱其事件。 還有一些鎖可以解決潛在的多線程問題。

public class GroupSourceOfEvents : ISourceOfEvents
{
    private object SourceOfEventsGroupLock = new object();

    private IList<ISourceOfEvents> _SourceOfEventsGroup;

    public IEnumerable<ISourceOfEvents> SourceOfEventsGroup
    {
        get { return _SourceOfEventsGroup; }
    }

    public void AddSource(ISourceOfEvents source)
    {
        lock (SourceOfEventsGroupLock)
        {
            source.SomethingHappened1 += _SomethingHappened1;
            source.SomethingHappened2 += _SomethingHappened2;
            _SourceOfEventsGroup.Add(source);
        }
    }

    public void RemoveSource(ISourceOfEvents source)
    {
        lock (SourceOfEventsGroupLock)
        {
            source.SomethingHappened1 -= _SomethingHappened1;
            source.SomethingHappened2 -= _SomethingHappened2;
            _SourceOfEventsGroup.Remove(source);
        }
    }

    private Action<EventInfo1> _SomethingHappened1;
    public event Action<EventInfo1> SomethingHappened1
    {
        add
        {
            lock (SourceOfEventsGroupLock)
            {
                _SomethingHappened1 += value;

                foreach (var s in SourceOfEventsGroup)
                {
                    s.SomethingHappened1 += value;
                }
            }
        }
        remove
        {
            lock (SourceOfEventsGroupLock)
            {
                _SomethingHappened1 -= value;

                foreach (var s in SourceOfEventsGroup)
                {
                    s.SomethingHappened1 -= value;
                }
            }
        }
    }

    private Action<EventInfo2> _SomethingHappened2;
    public event Action<EventInfo2> SomethingHappened2
    {
        add
        {
            lock (SourceOfEventsGroupLock)
            {
                _SomethingHappened2 += value;

                foreach (var s in SourceOfEventsGroup)
                {
                    s.SomethingHappened2 += value;
                }
            }
        }
        remove
        {
            lock (SourceOfEventsGroupLock)
            {
                _SomethingHappened2 -= value;

                foreach (var s in SourceOfEventsGroup)
                {
                    s.SomethingHappened2 -= value;
                }
            }
        }
    }
    // {...}

    public GroupSourceOfEvents(IList<ISourceOfEvents> sourceOfEventsGroup)
    {
        _SourceOfEventsGroup = sourceOfEventsGroup;
    }
}

編輯:我試圖通過重新排列段落,改寫句子的句子和刪除未完成的句子來清理我的答案。 內容保持不變。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM