Reputation: 27045
I'm porting over some old code from AS3 (via Haxe) to C#.
Parts of the code are transpiled, others I have manually rewritten in C#. One of these parts is the event dispatching.
I have event listeners registering to an event dispatcher, all listeners have a signature like this:
public void handleSomething(Event e)
// they may also use a subclass of Event as a parameter
public void handleAnother(MouseEvent e)
The events keep a small amount of data and a type:
public class Event {
public const string ENTER_FRAME = "enter_frame";
public const string RESIZE = "resize";
public const string CHANGE = "change";
readonly string type;
public Event(string type) {
this.type = type;
}
}
I keep a list keyed on the particular event type (a string, due to legacy reasons), once an event is dispatched I find the appropriate handlers keyed with that string and call the them with the event data.
I am currently using reflection to do this, but it is proving to be prohibitively slow. I have found several threads that share this problem.
My particular issue is that the method signature varies, if it was always an Event
as a parameter I could use the solutions provided, but alas.
I'd be fine with trading some memory/time at setup to get subsequent calls to be faster. I can get a reference to the method and work out what type it expects, but I'm unsure how to store and call this later?
Upvotes: 0
Views: 1109
Reputation: 2244
You can create and compile LINQ expression for each handler method and cache it for future use.
public class CompiledDelegate
{
// Assume that there is one one method per handler class type, add method name to dictionary key if necessary
private static Dictionary<Type, CompiledDelegate> _Cache = new Dictionary<Type, CompiledDelegate>();
public static CompiledDelegate Get(Type handlerType, string methodName)
{
CompiledDelegate result;
if (!_Cache.TryGetValue(handlerType, out result))
{
var method = handlerType.GetMethod(methodName);
// determine type of single method parameter
var paramType = method.GetParameters().Single().ParameterType;
// create expression tree (object h, object p) => ((handlerType)h).MethodName((paramType)p)
var exprHandler = Expression.Parameter(typeof(object), "h");
var exprParam = Expression.Parameter(typeof(object), "p");
var lambda = Expression.Lambda(
Expression.Call(
Expression.TypeAs(exprHandler, handlerType), // instance, cast object to handlerType
method, // method
Expression.TypeAs(exprParam, paramType) // parameter, cast object to paramType
),
exprHandler, exprParam // lamda params
);
result = new CompiledDelegate()
{
Method = method,
// compile expression
Compiled = (Action<object, object>)lambda.Compile()
};
_Cache.Add(handlerType, result);
}
return result;
}
public MethodInfo Method { get; private set; }
public Action<object, object> Compiled { get; set; }
}
Once you have hander instance, you can call its method via compiled delegate:
CompiledDelegate.Get(handler.GetType(), "handlerSomething").Compiled(handler, mouseEvent)
You can pre-generate CompiledDelegate for each of handlers and add to the dispatch table together with handlers themselves.
Calling method via compiled delegate (once it's compiled of course) is approximately 10 times faster then calling same method via reflection.
Upvotes: 1