Optimizing multiple dispatch notification algorithm in C#?
- by Robert Fraser
Sorry about the title, I couldn't think of a better way to describe the problem. Basically, I'm trying to implement a collision system in a game. I want to be able to register a "collision handler" that handles any collision of two objects (given in either order) that can be cast to particular types. So if Player : Ship : Entity and Laser : Particle : Entity, and handlers for (Ship, Particle) and (Laser, Entity) are registered than for a collision of (Laser, Player), both handlers should be notified, with the arguments in the correct order, and a collision of (Laser, Laser) should notify only the second handler.
A code snippet says a thousand words, so here's what I'm doing right now (naieve method):
public IObservable<Collision<T1, T2>> onCollisionsOf<T1, T2>()
where T1 : Entity
where T2 : Entity
{
Type t1 = typeof(T1);
Type t2 = typeof(T2);
Subject<Collision<T1, T2>> obs = new Subject<Collision<T1, T2>>();
_onCollisionInternal += delegate(Entity obj1, Entity obj2)
{
if (t1.IsAssignableFrom(obj1.GetType()) && t2.IsAssignableFrom(obj2.GetType()))
obs.OnNext(new Collision<T1, T2>((T1) obj1, (T2) obj2));
else if (t1.IsAssignableFrom(obj2.GetType()) && t2.IsAssignableFrom(obj1.GetType()))
obs.OnNext(new Collision<T1, T2>((T1) obj2, (T2) obj1));
};
return obs;
}
However, this method is quite slow (measurable; I lost ~2 FPS after implementing this), so I'm looking for a way to shave a couple cycles/allocation off this.
I thought about (as in, spent an hour implementing then slammed my head against a wall for being such an idiot) a method that put the types in an order based on their hash code, then put them into a dictionary, with each entry being a linked list of handlers for pairs of that type with a boolean indication whether the handler wanted the order of arguments reversed. Unfortunately, this doesn't work for derived types, since if a derived type is passed in, it won't notify a subscriber for the base type. Can anyone think of a way better than checking every type pair (twice) to see if it matches?
Thanks,
Robert