c# - Unity c# - 无法从“方法组”转换为“操作”'
问题描述
情况
我的统一应用程序中有一个基本的事件管理系统,我试图让它让我调度子类的自定义事件,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;
}
}
...
PinchScale.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);
当然,在调度事件时,您还必须将它们的参数转换为它们接受的类型,但这一次的责任将是 on 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
(甲级)
class A
{
public void DoThing(DerivedEvent arg)
{
Console.WriteLine("Reacting to event!");
}
}
推荐阅读
- javascript - 使用 vanilla JavaScript 根据索引移动元素
- postgresql - HAProxy, PGSQL with SSL and multiple clusters under single port
- javascript - 这段代码中setTimeout的用途是什么
- c# - 在字符串类型的用户控件上创建依赖属性,但在属性浏览器中显示为组合框
- angular - 如何在angular7中开发在两个不同组件之间切换的选项卡?
- java - 错误:JAVA_HOME 设置为 libGDX 中的无效目录
- jdbc - 使用 Simba JDBC Driver for BigQuery 将 BigDecimal 值插入 NUMERIC 列时,为什么会丢失数字精度?
- keras - 卷积一维计算它实际上是如何工作的?
- c# - 中继器在将其用作自定义寻呼机时变得越来越大,在gridview中进行自定义分页
- c - 信号量中的 P(&sem) 和 V(&sem) 如何影响代码?