繁体   English   中英

Unity c# - 无法从“方法组”转换为“动作”<EventManager.Event> &#39;

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM