One of the “tasty” features of .NET, and in particular C #, is Reflection (reflexion) - a rich set of classes for working with data types, their properties, methods, and fields at runtime.
But for such a wide functionality you have to pay performance.
The method call from the code is executed almost a hundred times faster than the call through reflex.
And if we don’t know in advance which particular class will be used and which method will be called or don’t want to be tied to a specific library, then working through reflection can greatly reduce application performance.
In this article I would like to talk about the use of dynamic methods for such purposes.
Suppose that we know in advance only the number of arguments of the method we want to call. That will be enough for us ...
Let's write a test class whose method we will call.
')
using System;
namespace AppForBlog1 {
class TestClass {
int index;
public int Index { get { return index; } set { index = value ; } }
public int UpdateIndex( int index) {
this .index = index;
return this .index * 2;
}
}
}
* This source code was highlighted with Source Code Highlighter .
What do we want? We want to get a delegate of a new method, which should receive an input of an instance of the required class and parameters of the called method and return the result of the called method.
You must first define a delegate.
public delegate object MethodDelegate( object inst, object param);
Now we will decide what information we need to call the method: the name of the class, the name of the method, the signature of the method, and the type of the class from which this method will be called (explained below).
We turn to the most interesting.
To work with reflections and create a dynamic method, we need to use the following namespaces, respectively System.Reflection and System.Reflection.Emit.
First, we need to get information about the method we will call. We will do this by means of ordinary reflexion.
MethodInfo mi = _.GetMethod( _, BindingFlags.Public | BindingFlags.Instance, null , new Type[] { _ }, null );
Secondly, you need to create an instance of the DynamicMethod class.
DynamicMethod dm = new DynamicMethod( __, _, _, ___ , new Type[] { __1__, __2__ }, __);
It should be clarified here that the method cannot exist without any class, therefore we indicate the type of owner_class to which the method will be attached.
We will create a static method, so MethodAtributes.Public and MethodAttributes.Static should be specified in method_ attributes, and CallingConventions.Standard should be specified in method_call.
Further, our method should do something. Therefore, we create an IL generator and enter instructions into it.
ILGenerator gen = dm.GetILGenerator();
We call the method is not static, so the first argument should be an instance of the class, followed by the parameters.
Because If the instance and parameters come in a method packed into an object, then they need to be unpacked (execute unboxing).
After calling the method, we need to pack the result into object and return it to the calling code.
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Unbox_Any, _);
gen.Emit(OpCodes.Ldarg_1);
gen.Emit(OpCodes.Unbox_Any, _);
gen.Emit(OpCodes.Callvirt, mi);
gen.Emit(OpCodes.Box, _);
gen.Emit(OpCodes.Ret);
The method is ready. It remains only to receive a delegate.
MethodDelegate method = (MethodDelegate)dm.CreateDelegate( typeof (MethodDelegate));
Now we can use this delegate as many times as necessary to call the method we need.
We collect the above in class.
using System;
using System.Reflection;
using System.Reflection.Emit;
namespace AppForBlog1 {
public class ReflectClass {
public static MethodDelegate GetMethodDelegate(Type owner, Type instance, string methodName, Type returnType, Type parameterType) {
//
MethodInfo mi = instance.GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance, null , new Type[] { parameterType }, null );
if (mi == null ) return null ;
//
DynamicMethod dm = new DynamicMethod(methodName + "Wrapper" ,
MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard,
typeof ( object ), new Type[] { typeof ( object ), typeof ( object ) }, owner, false );
// IL-
ILGenerator gen = dm.GetILGenerator();
//
gen.Emit(OpCodes.Ldarg_0);
// object
gen.Emit(OpCodes.Unbox_Any, instance);
//
gen.Emit(OpCodes.Ldarg_1);
// object
gen.Emit(OpCodes.Unbox_Any, parameterType);
//
gen.Emit(OpCodes.Callvirt, mi);
// object
gen.Emit(OpCodes.Box, returnType);
//
gen.Emit(OpCodes.Ret);
//
return (MethodDelegate)dm.CreateDelegate( typeof (MethodDelegate));
}
}
public delegate object MethodDelegate( object inst, object param);
}
* This source code was highlighted with Source Code Highlighter .
Well, it's time to check the performance of this class.
using System;
using System.Collections. Generic ;
using System.Linq;
using System.Text;
using NUnit.Framework;
namespace AppForBlog1 {
[TestFixture]
public class TestFixtureClass {
[Test]
public void FastReflectionTest() {
// " "
TestClass ts = new TestClass();
//
Assert.AreEqual(4, ts.UpdateIndex(2));
Assert.AreEqual(2, ts.Index);
//
MethodDelegate updateIndex = ReflectClass.GetMethodDelegate( typeof (TestFixtureClass), typeof (TestClass), "UpdateIndex" , typeof ( int ), typeof ( int ));
ts.Index = 1;
Assert.AreEqual(1, ts.Index);
//
Assert.AreEqual(16, updateIndex(ts, 8));
Assert.AreEqual(8, ts.Index);
}
}
}
* This source code was highlighted with Source Code Highlighter .
Everything is working!..
When used in real-world applications, types will have to be obtained not through typeof, but correspondingly again through reflection. But this will need to be done once and then use only the delegate, whose call speed is just 2 times slower than a direct call.
Good luck!
PS This method also works for class properties, since any property is two methods: set and get. And they can be obtained from property information (PropertyInfo).