Hi All! Today we're going to explore few ways to perform "dynamic method invocation" with .NET Core.
As some of you know already, a while ago I started working on an open-source project, OpenSleigh. It's a Saga management library for .NET Core applications.
I have been focusing on it a lot in the last period, and this, unfortunately, led me to be less diligent with my blog. I have several articles in my backlog, looking for the right time to jump off the hat.
Anyways, while working on the first prototypes of OpenSleigh (BTW, make sure to at least fork or star the repository!), I had to face a bunch of times an interesting problem.
Let me try to summarize it very quickly:
What if we have to call a method on some instance, but the only thing we know is the method signature and not the class type?
It's a tricky situation, but luckily for us, .NET has a few ways to get to the destination.
The problem now is: which one is the best?
So I decided to give it a try and write some benchmarks. I've tested:
- direct method invocation
- MethodInfo.Invoke
- Delegate.DynamicInvoke
- Func<> invocation
- dynamic cast
Of course, *direct method invocation *is used as a comparison, a baseline for all the other techniques.
Let's suppose we have this small class here:
public class Foo
{
public int Bar(int a, int b, bool c) => a + (c ? b : 0);
}
And we want to call Bar() *on an instance, but all we have is an *object. Let's take a look at each technique.
MethodInfo.Invoke
We simply start by storing a MethodInfo reference to Bar() and then we simply invoke it:
object fooInstance = ...; // we get this from somewhere else
MethodInfo barMethod = ClassType.GetMethod(nameof(Foo.Bar));
barMethod.Invoke(fooInstance, new[] { (object)1, (object)2, (object)false });
Delegate.DynamicInvoke
In this case, instead, we start off by getting a MethodInfo *reference, but we wrap it into a typed *Delegate:
object fooInstance = ...; // we get this from somewhere else
MethodInfo barMethod = ClassType.GetMethod(nameof(Foo.Bar));
var delegateType = Expression.GetDelegateType(typeof(Foo), typeof(int), typeof(int), typeof(bool), typeof(int));
var @delegate = Delegate.CreateDelegate(delegateType, barMethod);
@delegate.DynamicInvoke(new[] { fooInstance, (object)1, (object)2, (object)false });
Func<> invocation
Similar to the previous one, but we also cast the Delegate *to a *Func<>:
object fooInstance = ...; // we get this from somewhere else
MethodInfo barMethod = ClassType.GetMethod(nameof(Foo.Bar));
var delegateType = Expression.GetDelegateType(typeof(Foo), typeof(int), typeof(int), typeof(bool), typeof(int));
var func = (Func<Foo, int, int, bool, int>)Delegate.CreateDelegate(delegateType, barMethod);
func(fooInstance as Foo, 1, 2, false);
This one seems a little bit "shady", since we actually know that the instance is of type Foo. But still, I decided to add it to the group since it might still come useful.
Dynamic cast
This one is the easiest to code (after the direct invocation of course), as it's basically just a cast to dynamic and a method call:
object fooInstance = ...; // we get this from somewhere else
dynamic dynamicFoo = fooInstance as dynamic;
dynamicFoo.Bar(1, 2, false);
So, after all this fuss, who's the winner? I guess an image is worth thousand words:
So as you can see, calling a method directly, is definitely the fastest way. Followed immediately by the "shady" *Func<> *call and then the *dynamic *cast.
As usual, I've pushed the code to GitHub, so feel free to run your experiments and let me know!
Top comments (0)