[英]Unity c# - cannot convert from 'method group' to 'Action<EventManager.Event>'
我在我的统一应用程序中有一个基本的事件管理系统,我试图让它让我调度自定义事件,子类EventManager.Event
而不是必须在我的处理程序中投射自定义事件。
当前工作示例
//PinchScale.cs
int zoomLevel = Random.Range(1, 10);
EventManager.TriggerEvent(new ZoomEvent(zoomLevel));
// Earth.cs
void ZoomEventHandler(EventManager.Event e)
{
int zoomLevel = ((PinchScale.ZoomEvent)e).zoomLevel;
// do some stuff
}
所需示例
void ZoomEventHandler(PinchScale.ZoomEvent e)
{
this.LoadTiles(e.zoomLevel);
}
所需示例的错误
Assets\Scripts\Earth.cs(23,52): error CS1503: Argument 2: cannot convert from 'method group' to 'Action<EventManager.Event>'
是否可以在 c# 中使用所需的示例? 我这么认为是因为PinchScale.ZoomEvent
EventManager.Event
子类。
下面的附加代码
事件管理器.cs
...
public static void StartListening(string eventType, Action<EventManager.Event> listener)
{
if (!EventManager.instance)
{
Debug
.LogError("EventManager has not been initted. Is there one added to your scene yet?");
return;
}
EventListenerPair pair = new EventListenerPair(eventType, listener);
EventManager.instance.eventDictionary.Add (pair);
}
public static void StopListening(string eventType, Action<EventManager.Event> listener)
{
if (
EventManager.instance == null ||
EventManager.instance.eventDictionary.Count <= 0
) return;
Array d = EventManager.instance.eventDictionary.ToArray();
foreach (EventListenerPair e in d)
{
if (e.eventType == eventType && e.listener == listener)
EventManager.instance.eventDictionary.Remove(e);
}
//Debug.Log("event listener count: " + EventManager.instance.eventDictionary.Count);
}
public static void TriggerEvent(EventManager.Event e)
{
if (
EventManager.instance == null ||
EventManager.instance.eventDictionary.Count <= 0
) return;
Array d = EventManager.instance.eventDictionary.ToArray();
foreach (EventListenerPair pair in d)
{
// if (pair.eventType == e.type) pair.listener.Invoke(e);
if (pair.eventType == e.type) pair.listener(e);
}
}
public class Event
{
public string type;
public Event(string type)
{
this.type = type;
}
}
class EventListenerPair
{
public string eventType;
public Action<EventManager.Event> listener;
public EventListenerPair(string eventType, Action<EventManager.Event> listener)
{
this.eventType = eventType;
this.listener = listener;
}
}
...
捏比例尺.cs
...
public class ZoomEvent : EventManager.Event
{
public int zoomLevel;
public ZoomEvent(int zoomLevel = 0) :
base(PinchScale.ZOOM_EVENT)
{
this.zoomLevel = zoomLevel;
}
}
...
地球.cs
...
void Start() {
EventManager.StartListening(PinchScale.ZOOM_EVENT, this.ZoomEventHandler);
}
...
void ZoomEventHandler(PinchScale.ZoomEvent e)
{
int zoomLevel = ((PinchScale.ZoomEvent)e).zoomLevel;
// do some stuff
}
没错, PinchScale.ZoomEvent
EventManager.Event
子类,但Action<EventManager.Event>
不是Action<PinchScale.ZoomEvent>
子类。 如果它以这种方式工作,那将意味着多重继承(因为泛型类会从它自己的基类(在这种情况下为Delegate
?)和另一个泛型类( Action<EventManager.Event>
))派生,这是,我相信,在 C# 中是不允许的。
但是,您可以使 StartListening 和 StopListening 方法通用,如下所示:
public static void StartListening<T>(string eventType, Action<T> listener) where T : EventManager.Event
{
//handle the type differences here
}
然后你可以像你想要的那样使用它,但你必须在调用时明确声明泛型的类型,因为它不能被推断出来:
EventManager.StartListening<PinchScale.ZoomEvent>(PinchScale.ZOOM_EVENT, this.ZoomEventHandler);
当然,在调度事件时,您还必须将它们的参数转换为它们接受的类型,但这次的责任将在EventManager
,而不是在侦听器上。
一个快速模型(我将 type 参数添加到 Dispatch 以强制用户明确说明应该调度什么事件。这只是我的偏好,您可以在没有它的情况下转换事件):
public class BaseEvent {}
public class DerivedEvent : BaseEvent {}
public class AnotherDerivedEvent : BaseEvent {}
class EventManager
{
static Dictionary<BaseEvent, List<Delegate>> subscribers = new Dictionary<BaseEvent, List<Delegate>>();
public static void StartListening<T>(BaseEvent @event, Action<T> arg) where T : BaseEvent
{
Action<BaseEvent> convertedAction = (BaseEvent be) => arg((T)be);
if (!subscribers.ContainsKey(@event))
subscribers.Add(@event, new List<Delegate>() { arg });
else
subscribers[@event].Add(arg);
}
public static void Dispatch<T>(BaseEvent @event) where T : BaseEvent
{
if (subscribers.ContainsKey(@event))
foreach (var sub in subscribers[@event])
{
if (sub.GetType().GenericTypeArguments[0] != typeof(T)) //check that we didn't call with wrong type
throw new ArgumentException("Event not matching subscriber!");
var action = (sub as Action<T>);
action((T)@event);
}
}
}
和用法:
var a = new A();
var e = new DerivedEvent();
var e2 = new AnotherDerivedEvent();
EventManager.StartListening<DerivedEvent>(e, a.DoThing);
EventManager.Dispatch<DerivedEvent>(e); // output: Reacting to event!
EventManager.Dispatch<AnotherDerivedEvent>(e); // this line throws, because we used the wrong type argument
(A类)
class A
{
public void DoThing(DerivedEvent arg)
{
Console.WriteLine("Reacting to event!");
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.