我通过按照教程制作了一个简单的事件系统,侦听器的注册和触发事件运行良好,但是我无法从中删除任何侦听器。
delegate void EventListener(EventInfoBase eventInfo);
Dictionary<System.Type, List<EventListener>> eventListeners;
public void RegisterListener<T>(System.Action<T> listener) where T : EventInfoBase
{
System.Type eventType = typeof(T);
if (eventListeners == null)
{
eventListeners = new Dictionary<System.Type, List<EventListener>>();
}
if (!eventListeners.ContainsKey(eventType) || eventListeners[eventType] == null)
{
eventListeners[eventType] = new List<EventListener>();
}
EventListener wrapper = (ei) => { listener((T)ei); };
eventListeners[eventType].Add(wrapper);
}
public void UnregisterListener<T>(System.Action<T> listener) where T : EventInfoBase
{
System.Type eventType = typeof(T);
if (eventListeners == null)
{
return;
}
if (!eventListeners.ContainsKey(eventType) || eventListeners[eventType] == null)
{
return;
}
EventListener wrapper = (ei) => { listener((T)ei); };
EventListener toRemove = eventListeners[eventType].Find(x => x.Equals(wrapper));
//EventListener toRemove = eventListeners[eventType].Find(x => x.Target == wrapper.Target && x.Method == wrapper.Method);
if (toRemove != null)
{
eventListeners[eventType].Remove(toRemove); // Never gets called
}
}
这就是它的称呼(单身):
EventsSystem.Instance.RegisterListener<EventInfoWin>(OnWin);
EventsSystem.Instance.UnregisterListener<EventInfoWin>(OnWin);
因此,我希望将侦听器从适当的列表中删除,但它会保留在那里。 UnregisterListener方法不执行任何操作。有什么方法可以快速修复它而无需重写所有内容?
答案 0 :(得分:0)
您不能像以前那样使用包装委托。原因是,这正在添加时创建另一个“对象”,以后要删除它时将无法识别。 正如Jon Skeet所写,您可以直接将动作保存为对象。我已经对其进行了测试,但没有找到一种方法将EventInfoBase的 Action 放入EventInfoWin的 行为中。
这就是它的样子:
编辑:我再次创建了一个包装器,但是将原始操作作为令牌来再次找到它。
delegate void EventListener(EventInfoBase eventInfo);
private class EventWrapper
{
public EventListener Action { get; set; }
public object Token { get; set; }
}
Dictionary<System.Type, List<EventWrapper>> eventListeners = new Dictionary<System.Type, List<EventWrapper>>();
public void RegisterListener<T>(System.Action<T> listener) where T : EventInfoBase
{
System.Type eventType = typeof(T);
if (!eventListeners.ContainsKey(eventType) || eventListeners[eventType] == null)
{
eventListeners[eventType] = new List<EventWrapper>();
}
EventListener action = (ei) => { listener((T)ei); };
var wrapper = new EventWrapper() { Action = action, Token = listener };
eventListeners[eventType].Add(wrapper);
}
public void UnregisterListener<T>(System.Action<T> listener) where T : EventInfoBase
{
System.Type eventType = typeof(T);
if (!eventListeners.ContainsKey(eventType) || eventListeners[eventType] == null)
{
return;
}
var toRemove = eventListeners[eventType].FirstOrDefault(x => x.Token.Equals(listener));
if (toRemove != null)
{
eventListeners[eventType].Remove(toRemove);
}
}
答案 1 :(得分:0)
您的问题是,您在RegisterListener<T>
中创建了一个EventListener
类型的匿名包装方法,而在UnregisterListener<T>
中创建了一个另一个包装方法。这些包装器永远不会匹配。
您可以做的是将原始侦听器与包装器一起存储。这将允许您与原始侦听器匹配,但是可以执行包装器(要执行原始侦听器,您需要进行反射-因此需要包装器)。如果您从字典切换到元组列表,则可以通过一种简单的方式做到这一点:
delegate void EventListener(object eventInfo);
List<(System.Type Type, Delegate Listener, EventListener Wrapper)> eventListeners;
public void RegisterListener<T>(System.Action<T> listener)
{
System.Type eventType = typeof(T);
if (eventListeners == null)
{
eventListeners = new List<(System.Type, Delegate, EventListener)>();
}
if (!eventListeners.Any(entry => entry.Type.Equals(eventType) &&
entry.Listener.Equals(listener))) {
eventListeners.Add((eventType, listener, ei => listener((T)ei)));
}
}
public void UnregisterListener<T>(System.Action<T> listener)
{
System.Type eventType = typeof(T);
if (eventListeners == null)
{
return;
}
var toRemove = eventListeners.FirstOrDefault(entry => entry.Type.Equals(eventType) &&
entry.Listener.Equals(listener));
eventListeners.Remove(toRemove);
}
您可以尝试here。