I am trying to roll an event handling system in C# - in my case it's for a game in Unity but it's abstract enough to apply to any system.
A singleton class "EventManager" has a private Dictionary(System.Type,Dictionary(long, EventListener)) __listeners along with a public methods to Register, Unregister and ThrowEvent(EventInfo ei). The dictionary's key is a type derived from EventInfo, so there will be keys for EventInfoFoo, EventInfoBar and so forth, and these may not necessarily have the same fields.
I would also like to be able to only listen for specific conditions within these classes derived from EventInfo such as "only fire when ei.CreatureType==Animal" or "position.x between 1 and 5".
I have a working solution using Reflection, however its performance is just not good enough. My next idea was to have this filter be a delegate method passed on by the class registering the listener, but since I expect almost all, if not all filters to be equality/range checks, I wonder if there's a cleaner way of handling it.
Here are the pertaining classes:
EventListener:
public class EventListener {
public Dictionary<string, string> eventFilter;
public delegate void eventHandler(EventInfo ei);
public eventHandler Eh;
public EventListener( eventHandler evH,Dictionary<string, string> filter)
{
Eh= evH;
eventFilter = filter;
}}
EventInfo:
public class EventInfo {
public Object Caller;
public EventInfo (Object __caller)
{
Caller = __caller;
}
public EventInfo()
{ Caller = null; }
}
public class EventInfoExample : EventInfo
{
public int Testint;
public EventInfoExample(Object __caller)
{
Caller = __caller;
}
}
EventManager:
public class EventManager : MonoBehaviour {
private static EventManager __em;
public static EventManager Em
{
get { return EventManager.__em; }
}
private Dictionary<System.Type,Dictionary<long, EventListener>> __listeners;
private long __idcounter = 1;
private long getNewID()
{
long __ret = __idcounter;
__idcounter++;
return __ret;
}
//true on let through , false on block
private bool __doFilter(Dictionary<string,string>eventFilter , EventInfo ei)
{
// if no filters, accept
if (eventFilter == null || eventFilter.Count < 1)
return true;
System.Type __eit = ei.GetType();
FieldInfo[] __fields = ei.GetType().GetFields();
List<string> __fieldlist = __fields.Select(f => f.Name).ToList();
foreach (KeyValuePair<string,string> kvp in eventFilter)
{
if (__fieldlist.Contains(kvp.Key) == false)
Debug.LogError("Fieldlist for " + __eit.ToString() + " does not contain a field named " + kvp.Key);
//this is what we are filtering for
//TODO add support for operators, for now its just ==
if (__eit.GetField(kvp.Key).GetValue(ei).ToString() != kvp.Value)
return false;
}
return true;
}
public Object ThrowEvent(EventInfo ei)
{
Debug.Assert(__listeners != null);
Debug.Assert(ei != null);
if (__listeners.ContainsKey(ei.GetType()) == false)
return null;
//call all
foreach ( KeyValuePair<long,EventListener> __kvp2 in __listeners[ei.GetType()])
{
// apply listener filters
if (__doFilter(__kvp2.Value.eventFilter , ei))
{
Debug.Log("Invoking ID " + __kvp2.Key.ToString() + " for " + ei.GetType().ToString());
__kvp2.Value.Eh(ei);
}
}
return null;
}
public long Register(System.Type eventType,EventListener el)
{
Debug.Assert(eventType.IsSubclassOf(typeof(EventInfo)));
Debug.Assert(el != null);
Debug.Assert(__listeners != null);
// if we dont have a key for this type, create new dict, then add to dict
if (__listeners.ContainsKey(eventType) == false)
__listeners.Add(eventType, new Dictionary<long, EventListener>());
long __newID = getNewID();
//add to list
__listeners[eventType].Add(__newID, el);
return __newID;
}
public bool Unregister(long ID)
{
return true;
}
// Use this for initialization
void Start () {
// pop singleton
EventManager.__em = this;
}
// Update is called once per frame
void Update () {
}}
Aucun commentaire:
Enregistrer un commentaire