I am trying to implement my own messaging system for a Unity game. I have a basic version working - a very simplified example of this is as follows:
// 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.
}
This all works fine. What I would like to do now is implement some "magic" to allow the message handler to be called with the actual derived type, like this:
private bool MessageHandler(MyMessage message) {
// Handle the message.
}
This would mean that the message handling code across thousands of scripts will not need to bother casting the Message object to the correct derived type - it will already be of that type. It would be far more convenient. I feel like this could be possible to achieve somehow using generics, delegates, expression trees, covariance and/or contravariance, but I'm just not getting it!
I've been trying a lot of different things, and feel like I am getting close, but I just can't get there. These are the two partial solutions that I've been able to come up with:
// 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.
}
}
Partial solution 1 works fine, but it uses reflection, which isn't really an option considering the performance - this is a game, and the messaging system will be used a lot. Partial solution 2 works, but only as long as the T generic parameter is available. The messaging system will also have a queue of Message objects that it will process, and T will not be available there.
Is there any way to achieve this? I would greatly appreciate any help that anyone could offer!
One final thing to note is that I am using Unity, which uses Mono - .NET 4 is not an option, and as far as I know this rules out using "dynamic".