Under normal circumstances invoking a method is no big deal, but what if we are doing some metaprogramming and don’t know who or what to call at compile time? Well, there are quite a few options for this, but it can be a bit hard to pick which to use. Here’s a quick overview of the most common options,
- dynamic keyword – when we know the method but you don’t know the type at compile time.
- Reflection – when we may not know the method or type at compile time and don’t make the call often.
- Expression Trees – when we may not know the method or type at compile time and make the call often.
- Delegate.CreateDelegate -when we only have one instance to invoke against or are dealing with a static method and know the signature of the delegate.
It usually comes down to reflection vs. expression trees with the decision made based on performance. Let’s take a look at the performance in a more graphical format (generated from an unscientific benchmark),
From the chart it looks like we should always being using Delegate.CreateDelegate! However, keep in mind the numbers are skewed by it only running against a single instance or a static method. Delegate.CreateDelegate would be off the chart if we included it running against multiple instances, running about 100x slower than native time. Anyway, from the chart we can see that expression trees are really close to native execution at about 2x native time whereas Reflection is way at the top of the chart, at about 10x native time. You’ll also see RuntimeAction on the chart, it’s a wrapper around the expression tree implementation that does some safety checks just to show that they can be included at little cost.
So, expression trees, how would we use them to invoke a method that was discovered at runtime? Like so,
//Building a delegate with expression trees
// The delegate will take an instance of a type and
// call it's DoSomething method with a string value as a parameter.
// BTW, you should cache the result.
//TestTarget = the type of the class to call the method on.
var method = typeof(TestTarget).GetMethod("DoSomething");
//Representations of our lambda parameters.
var pTarget = Expression.Parameter(typeof(TestTarget), "target");
var pValue = Expression.Parameter(typeof(string), "value");
//Build the call, passing the instance, method, and argument.
var body = Expression.Call(pTarget, method, pValue);
//Wrap the all up to make the lambda and relate it to a delegate type.
var lambda = Expression.Lambda<Action<TestTarget, string>>(body, pTarget, pValue);
//Compile it! can take a while for more complex methods.
var compiledDelegate = lambda.Compile();
//Using the resulting delegate
Not too bad, eh? This ends up as second nature once it’s used a few times.