How to dynamically invoke delegates with known parameter base type?
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".
No comments:
Post a Comment