向事件系统传递自定义数据/值

本文关键字:数据 自定义 事件系统 | 更新日期: 2024-10-23 10:35:28

我有两个类,Input和"EventSystem"。EventSystem是一个内部(非系统相关)类,用于处理"内部应用程序事件"。Input是一个依赖类(依赖于系统),它处理按键/按钮事件,并将它们映射到按键事件和"EventSystem"。我将向您展示我目前是如何传递数据的,它内部看起来非常干净,但外部非常脏。有人知道传递自定义值的更好方法或更简单的方法吗?

EventSystem:

// Raisable Events
public enum EventType { Action, Cancel };
// Base for custom arguments
public class EventArguments
    {
        public double time;
        public EventArguments(double _time)
        {
            time = _time;
        }
    }

// Event Delegate (invoked on raise)
public delegate void Event(EventArguments eventArgs);
// full class
static class EventSystem
{
    private static Dictionary<EventType, Event> eventMapping = new Dictionary<EventType, Event>();
    public static void HookEvent(EventType eventType, Event _event)
    {
        if (eventMapping.ContainsKey(eventType))
        {
            eventMapping[eventType] += _event;
        }
        else
        {
            eventMapping.Add(eventType, _event);
        }
    }
    public static void RaiseEvent(EventType eventType, EventArguments args)
    {
        if (eventMapping.ContainsKey(eventType))
        {
            eventMapping[eventType].Invoke(args);
        }
        else
        {
            // do nothing
        }
    }
}

我的输入参数只是继承EventArguments。

// Inherits EventArguments (double time) and adds it's own, "bool pressed"
class KeyInputArguments : EventArguments
{
    public bool pressed;
    public KeyInputArguments(double time, bool _pressed) :
        base(time)
    {
        pressed = _pressed;
    }
}

当按下某个键时,它会触发键(输入)事件,然后检查该键是否映射到内部事件并引发它。一个单独的类(Config)处理将键映射/绑定到事件的所有配置。

// Raise on press
EventSystem.RaiseEvent(eventType, new KeyInputArguments(time, true));
// [...]
// Raise on release
EventSystem.RaiseEvent(eventType, new KeyInputArguments(time, false));

最后,为了触发事件,我们必须注册事件的密钥(这是"外部"代码)

// Hook our "Enter" function into the Action event
EventSystem.HookEvent(EventType.Action, Enter);
// [...]
public void Enter(EventArguments eventArg)
{
    if (((KeyInputArguments)eventArg).pressed == false)
    {
        Error.Break();
    }
}

在我看到"((()"之前,一切都很好。这是我对C#和OOP编程知识有限的一个丑陋结果。

我无法更改Enter方法参数,因为Event委托明确要求EventArguments。(即使KeyInputArguments继承了它?)。我也不明白为什么将eventArg强制转换为KeyInputArguments需要这么多时间。

最后,我也尝试了这个(虽然我不太喜欢)

KeyInputArguments keyInputArguments = (KeyInputArguments)eventArg;
            if (keyInputArguments.pressed == false)

我需要自定义数据的原因是,我计划从多种形式的输入(如游戏板)接收输入。这意味着我可以将依赖于系统的数据(游戏板设备信息)处理成独立的参数。这将依赖于系统的数据限制在我的Input类中,同时在内部独立地利用我的事件系统对于我正在做的事情,有更好的方法吗

向事件系统传递自定义数据/值

将此代码放在项目中的某个位置:

public static class SomeClassName
{
    public static T CastTo<T>(this object source)
    {
        return (T)source;
    }
}

这使您能够编写

if(!eventArg.CastTo<KeyInputArguments>().pressed)
    Error.Break();

我真的很喜欢这个,因为它保留了事物从左到右的顺序。这在写linq的时候特别好。

在回答您的问题"即使KeyInputArguments继承了它?"时。这里的问题是,您定义了一个使用基类的通用方法,它不知道继承的类。您需要在该方法上使用泛型来解决该问题,例如将事件类型作为泛型参数传入

public static void HookEvent<TEventType>(....)

我把MikeKulls的答案"HookEvent"和一个朋友的一些建议混合在一起,得出了这个:

// Event types to raise.
enum EventType { Action, Cancel };
// Base abstract class to use in the generic type field
abstract class BaseEvent
{
    public double time;
    protected BaseEvent(double _time)
    {
        time = _time;
    }
}
// Event delegate, T is a sub of BaseEvent that contains the data we need to send
delegate void EventDelegate<T>(T eventClass) where T : BaseEvent;
// Event class
static class EventSystem
{
    // EventClass is the storage of all the generic types and their mapping to an event delegate.
    private static class EventClass<T> where T : BaseEvent
    {
        public static Dictionary<EventType, EventDelegate<T>> eventMapping = new Dictionary<EventType, EventDelegate<T>>();
    }
    /// <summary>
    /// Hooks an event to a delegate for use with EventSystem.RaiseEvent
    /// </summary>
    /// <typeparam name="T">Sub-class of BaseEvent that your delegate will be receiving</typeparam>
    /// <param name="eventType">Type of event to hook to</param>
    /// <param name="_event">The callback</param>
    public static void HookEvent<T>(EventType eventType, EventDelegate<T> _event) where T : BaseEvent
    {
        if (EventClass<T>.eventMapping.ContainsKey(eventType))
        {
            EventClass<T>.eventMapping[eventType] += _event;
        }
        else
        {
            EventClass<T>.eventMapping.Add(eventType, _event);
        }
    }
    /// <summary>
    /// Raises an event and calls any callbacks that have been hooked via HookEvent
    /// </summary>
    /// <typeparam name="T">The sub-class of BaseEvent you are sending</typeparam>
    /// <param name="eventType">Type of event you are raising</param>
    /// <param name="args">A subclass of BaseEvent containing the information to pass on</param>
    public static void RaiseEvent<T>(EventType eventType, T args) where T : BaseEvent
    {
        if (EventClass<T>.eventMapping.ContainsKey(eventType))
        {
            EventClass<T>.eventMapping[eventType].Invoke(args);
        }
        else
        {
            // do nothing
        }
    }
}

我这样设置输入:

class KeyEvent : BaseEvent
    {
        public bool pressed;
        public KeyEvent(double _time, bool _pressed)
            : base(_time)
        {
            pressed = _pressed;
        }
    }
// [...]
// Key down
EventSystem.RaiseEvent<KeyEvent>(eventType, new KeyEvent(time, true));
// Key up
EventSystem.RaiseEvent<KeyEvent>(eventType, new KeyEvent(time, false));

我这样设置处理程序/代理:

EventSystem.HookEvent<KeyEvent>(EventType.Action, Enter);
// [...]
public void Enter(KeyEvent keyEvent)
{
    if (keyEvent.pressed == false)
    {
        Error.Break();
    }
}