我有两个类,Input和“EventSystem”。 EventSystem是一个内部(非系统相关)类,用于处理“内部应用程序事件”。输入是依赖类(取决于系统)并处理键/按钮事件并将它们映射到键事件和“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类,同时将我的事件系统内部作为独立的。 对于我正在做的事情有没有更好的方法?
答案 0 :(得分:2)
将此代码放在项目的某个地方:
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>(....)
答案 1 :(得分:0)
我最终混合了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();
}
}