我正在尝试为 Unity 游戏实现我自己的消息传递系统。我有一个基本版本的工作 - 一个非常简化的例子如下:
// Messaging class:
private Dictionary<Type, List<Func<Message, bool>>> listeners; // Set up by some other code.
public void AddListener<T>(Func<Message, bool> listener) where T : Message {
this.listeners[typeof(T)].Add(listener);
}
public void SendMessage<T>(T message) where T : Message {
foreach (Func<Message, bool> listener in this.listeners[typeof(T)]) {
listener(message);
}
}
// Some other class:
private void Start() {
messaging.AddListener<MyMessage>(this.MessageHandler); // Subscribe to messages of a certain type.
}
private bool MessageHandler(Message message) { // We receive the message as the Message base type...
MyMessage message2 = (MyMessage)message; // ...so we have to cast it to MyMessage.
// Handle the message.
}
这一切都很好。我现在想做的是实现一些“魔术”以允许使用实际的派生类型调用消息处理程序,如下所示:
private bool MessageHandler(MyMessage message) {
// Handle the message.
}
这意味着跨数千个脚本的消息处理代码将无需费心将 Message 对象转换为正确的派生类型 - 它已经属于该类型。会方便很多。我觉得这可以通过使用泛型、委托、表达式树、协变和/或逆变以某种方式实现,但我就是不明白!
我一直在尝试很多不同的事情,感觉我已经接近了,但我就是无法到达那里。这些是我能够提出的两个部分解决方案:
// Messaging class:
private Dictionary<Type, List<Delegate>> listeners; // Delegate instead of Func<Message, bool>.
public void AddListener<T>(Func<T, bool> listener) where T : Message { // Func<T, bool> instead of Func<Message, bool>.
this.listeners[typeof(T)].Add(listener);
}
public void SendMessage<T>(T message) where T : Message {
foreach (Delegate listener in this.listeners[typeof(T)]) {
listener.Method.Invoke(method.Target, new object[] { message }); // Partial solution 1.
((Func<T, bool>)listener)(message); // Partial solution 2.
}
}
部分解决方案 1 工作正常,但它使用反射,考虑到性能,这并不是一个真正的选择 - 这是一个游戏,消息传递系统将被大量使用。部分解决方案 2 有效,但前提是 T 通用参数可用。消息传递系统还将有一个消息对象队列,它将处理,而 T 将在那里不可用。
有什么办法可以做到这一点?我将非常感谢任何人可以提供的任何帮助!
最后要注意的一件事是我正在使用 Unity,它使用 Mono - .NET 4 不是一个选项,据我所知,这排除了使用“动态”的可能性。