2012-11-19 48 views
5

鑑於以下接口:收集不同泛型類型

public interface IEventHandler<in TEvent> where TEvent : IEvent 
{ 
    void Process(TEvent @event); 
} 

我可以使用哪些IEnumerable的類型來存儲的IEventHandler<TEvent>實現中TEvent是不同的集合?

即考慮以下3個實現:

public class BlahEvent1EventHandler : IEventHandler<Event1> 
{ 
    ... 
} 

public class WhateverEvent1EventHandler : IEventHandler<Event1> 
{ 
    ... 
} 

public class BlahEvent2EventHandler : IEventHandler<Event2> 
{ 
    ... 
} 

我可以做任何比對象的集合更好?

 var handlers = new List<object> 
          { 
           new BlahEvent1EventHandler(), 
           new WhateverEvent1EventHandler(), 
           new BlahEvent2EventHandler(), 
          }; 

BTW,看到了一些其他的答案提倡使用基本類型或繼承的非通用接口,但不能看到如何將在此情況下,增加了巨大的價值,除非量我失去了一些東西。是的,它可以讓我把它們全部添加到集合中,以一種稍微更安全的方式使用對象,但不會讓我遍歷它們並調用強類型的Process方法,而無需像使用對象一樣強制轉換。

public interface IEventHandler 
{   
} 

public interface IEventHandler<in TEvent> : IEventHandler where TEvent : IEvent 
{ 
    void Process(TEvent @event); 
} 

我還需要投如果我有IEnumerable<IEventHandler>IEnumerable<obect>

foreach (var handler in _handlers.Cast<IEventHandler<TEvent>>()) 
{ 
    handler.Process(@event); 
} 

,關於如何提高這個有什麼想法?

+0

提供了一個強類型接口的新的集合類,我不認爲有一個方法。很高興聽到這方面的一些有趣的投入,因爲我幾乎每天都會遇到同樣的問題。 –

+0

Protip:不要將您的變量命名爲與語言關鍵字相同的名稱。 – tomfanning

+1

由於列表中不同類型的項目需要使用不同參數類型的不同類型的「Process」方法,因此如何調用它們?請注意,此處不會出現方法重載解析,因爲列表元素的類型僅在運行時才知道。泛型的目的是提供編譯時已知的靜態類型。 –

回答

0

如果你需要通過你的處理程序枚舉並調用其過程接口成員,那麼你可以實現你的類是這樣的:

public class BlahEvent1EventHandler : IEventHandler<IEvent> 
{ 
    ... 
} 

public class WhateverEvent1EventHandler : IEventHandler<IEvent> 
{ 
    ... 
} 

public class BlahEvent2EventHandler : IEventHandler<IEvent> 
{ 
    ... 
} 

public class Event1 : IEvent 
{} 

public class Event2 : IEvent 
{} 

然後使用它們:

List<IEventHandler<IEvent>> list = new List<IEventHandler<IEvent>>(); 

list.Add(new BlahEvent1EventHandler()); 

foreach (IEventHandler<IEvent> eventHandler in list) 
{ 
eventHandler.Process(new Event1()); 
} 
+0

事件處理程序需要強類型事件,所以我無法繼承自IEventHandler 。這將需要我投入每個處理程序。 –

3

我認爲最好的辦法這裏是使用OfType擴展方法並保留你的List,假設事件的類型在編譯時已知;仍然會有演員陣容,但你不會這樣做,你只會得到實際可以處理該事件的條目。

1

可能你的設計不是最優的。嘗試將所有需要事件類型特定行爲的代碼移動到事件中,而不是在事件處理程序中實現它。即讓多態性爲你工作。

public interface IEventHandler 
{ 
    void Process(IEvent evt); 
} 

作爲示例,我們假設您需要根據事件特定的屬性創建消息。如果不建立該事件處理程序的內部消息,構造它在事件

public interface IEvent 
{ 
    string Message { get; } 
    ... 
} 

特定事件

public class TestEvent : IEvent 
{ 
    public string A { get; set; } // Event specific property 
    public string B { get; set; } // Event specific property 

    public string Message { get { return String.Format("{0} {1}", A, B); } } 
    // The event handler does not need to access A or B. 
} 

UPDATE

讓我們假設有一個辦法按您打算的方式定義清單

var handlers = new List<?>(); 

你會如何施展?

var handler = handlers[i]; 
// How to cast? 
((?)handler).Process((?)evt); 

也許一個更好的辦法是讓每個事件類型的一個列表

public static class EventHandler<in TEvent> : IEventHandler<TEvent> 
    where TEvent : IEvent 
{ 
    public static readonly List<IEventHandler<TEvent>> Handlers = 
     new List<IEventHandler<TEvent>>(); 

    ... 
} 

然後你就可以訪問一個事件處理這樣

SpecificEventType specEvent = ...; 
EventHandler<SpecificEventType>.Handlers[0].Process(specEvent); 

更新#2

一個完全不同的解決方案創建一個封裝弱類型列表並使用泛型方法

public class HandlerCollection : IEnumerable 
{ 
    private readonly List<object> _handlers = new List<object>(); 

    public void Add<TEvent>(IEventHandler<TEvent> handler) 
     where TEvent : IEvent 
    { 
     _handlers.Add(handler); 
    } 

    public IEventHandler<TEvent> Find<TEvent>() 
     where TEvent : IEvent 
    { 
     return _handlers 
      .OfType<IEventHandler<TEvent>>() 
      .FirstOrDefault(); 
    } 

    public IEventHandler<TEvent> Find<TEvent>(Func<IEventHandler<TEvent>, bool> predicate) 
     where TEvent : IEvent 
    { 
     return _handlers 
      .OfType<IEventHandler<TEvent>>() 
      .Where(predicate) 
      .FirstOrDefault(); 
    } 

    // Collection initializers can only be applied to types implementing IEnumerable 
    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return _handlers.GetEnumerator(); 
    } 
} 

測試

var handlers = new HandlerCollection { 
        new BlahEvent1EventHandler(), 
        new WhateverEvent1EventHandler(), 
        new BlahEvent2EventHandler() 
       }; 
IEventHandler<WhateverEvent1> eh = handlers.Find<WhateverEvent1>(); 
+0

這似乎是否定了首先使用事件的解耦方面。可能會有多個事件處理程序處理單個事件並添加一個新的操作,所需要的只是一個新的事件處理程序類,該類可以自動由IoC容器獲取。漂亮乾淨,實用。通過您的方法,事件類需要知道所有事情,並以其他方式處理事件,包括修改事件類。不知道我喜歡這個。 –

+0

@ The Flower Guy:好的,但是如果它取決於事件類型,你將如何調用正確的事件處理程序?您將需要一個涵蓋所有事件類型的switch語句。不是很面向對象。不同的事件處理程序仍然可以決定是否使用「消息」,但不應該依賴於「A」或「B」。 –

+0

這就是Func > eventHandlerFactory的用途。將它傳遞給事件,您將返回一組兼容的事件處理程序,以便您可以循環訪問它們。問題在於這個工廠被注入到課程中,以便在事件實際引發和處理時使用。我需要在啓動時注入Func。 Func基本上只是一種避免注入大量域事件實現似乎要做的實際IoC容器的方法。 –