Click here to Skip to main content
Click here to Skip to main content

Dynamic Code Generation vs Reflection

, 22 Jan 2007 CPOL
Rate this:
Please Sign up or sign in to vote.
A dynamic code generator for setting property and field values that outperforms reflection
Sample Image - DynamicCompilation.jpg

Introduction

This is an example of how to use the new DynamicMethod class in .NET 2.0 as an alternative to Reflection. Specifically, this example shows how to instantiate an object and get/set properties and fields on an object. As an added bonus, this code sample also allows getting and setting private fields and properties as well as instantiating objects with private constructors.

Background

At my current company we have built our own Object-Relational-Mapper (ORM), similar to nHibernate. In order to do this we were using reflection to set the properties/fields of an object with the values we retrieved from the database using a DataReader. Also, one of the features that was important to us was the ability to have “Read Only” properties on our objects. This involved setting the private field with our data mapper, instead of the public property. Unfortunately, reflection is a bit slow, especially when using it to set private values.

Solution

Fortunately, along came .NET 2.0 and the DynamicMethod class. This is a new class that allows one to create and compile code at run time. This approach is much faster then using reflection (see the times on the screen shot above). The down side to this approach is that you need to write your dynamic code using IL (as opposed to C#). But, with only a few lines of code we were able to create the desired effect.

Details

Here’s the important code in the included download (the other classes are just for demo).

public delegate object GetHandler(object source);
public delegate void SetHandler(object source, object value);
public delegate object InstantiateObjectHandler();

public sealed class DynamicMethodCompiler
{
    // DynamicMethodCompiler
    private DynamicMethodCompiler() { }

    // CreateInstantiateObjectDelegate
    internal static InstantiateObjectHandler CreateInstantiateObjectHandler(Type type)
    {
        ConstructorInfo constructorInfo = type.GetConstructor(BindingFlags.Public | 
               BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[0], null);

        if (constructorInfo == null)
        {
            throw new ApplicationException(string.Format("The type {0} must declare an
              empty constructor (the constructor may be private, internal, 
              protected, protected internal, or public).", type));
        }

        DynamicMethod dynamicMethod = new DynamicMethod("InstantiateObject", 
                MethodAttributes.Static | 
              MethodAttributes.Public, CallingConventions.Standard, typeof(object), 
                null, type, true);

        ILGenerator generator = dynamicMethod.GetILGenerator();
        generator.Emit(OpCodes.Newobj, constructorInfo);
        generator.Emit(OpCodes.Ret);
        return (InstantiateObjectHandler)dynamicMethod.CreateDelegate
                (typeof(InstantiateObjectHandler));
    }
    
    // CreateGetDelegate
    internal static GetHandler CreateGetHandler(Type type, PropertyInfo propertyInfo)
    {
        MethodInfo getMethodInfo = propertyInfo.GetGetMethod(true);
        DynamicMethod dynamicGet = CreateGetDynamicMethod(type);
        ILGenerator getGenerator = dynamicGet.GetILGenerator();

        getGenerator.Emit(OpCodes.Ldarg_0);
        getGenerator.Emit(OpCodes.Call, getMethodInfo);
        BoxIfNeeded(getMethodInfo.ReturnType, getGenerator);
        getGenerator.Emit(OpCodes.Ret);

        return (GetHandler)dynamicGet.CreateDelegate(typeof(GetHandler));
    }

    // CreateGetDelegate
    internal static GetHandler CreateGetHandler(Type type, FieldInfo fieldInfo)
    {
        DynamicMethod dynamicGet = CreateGetDynamicMethod(type);
        ILGenerator getGenerator = dynamicGet.GetILGenerator();

        getGenerator.Emit(OpCodes.Ldarg_0);
        getGenerator.Emit(OpCodes.Ldfld, fieldInfo);
        BoxIfNeeded(fieldInfo.FieldType, getGenerator);
        getGenerator.Emit(OpCodes.Ret);

        return (GetHandler)dynamicGet.CreateDelegate(typeof(GetHandler));
    }

    // CreateSetDelegate
    internal static SetHandler CreateSetHandler(Type type, PropertyInfo propertyInfo)
    {
        MethodInfo setMethodInfo = propertyInfo.GetSetMethod(true);
        DynamicMethod dynamicSet = CreateSetDynamicMethod(type);
        ILGenerator setGenerator = dynamicSet.GetILGenerator();

        setGenerator.Emit(OpCodes.Ldarg_0);
        setGenerator.Emit(OpCodes.Ldarg_1);
        UnboxIfNeeded(setMethodInfo.GetParameters()[0].ParameterType, setGenerator);
        setGenerator.Emit(OpCodes.Call, setMethodInfo);
        setGenerator.Emit(OpCodes.Ret);

        return (SetHandler)dynamicSet.CreateDelegate(typeof(SetHandler));
    }

    // CreateSetDelegate
    internal static SetHandler CreateSetHandler(Type type, FieldInfo fieldInfo)
    {
        DynamicMethod dynamicSet = CreateSetDynamicMethod(type);
        ILGenerator setGenerator = dynamicSet.GetILGenerator();

        setGenerator.Emit(OpCodes.Ldarg_0);
        setGenerator.Emit(OpCodes.Ldarg_1);
        UnboxIfNeeded(fieldInfo.FieldType, setGenerator);
        setGenerator.Emit(OpCodes.Stfld, fieldInfo);
        setGenerator.Emit(OpCodes.Ret);

        return (SetHandler)dynamicSet.CreateDelegate(typeof(SetHandler));
    }

    // CreateGetDynamicMethod
    private static DynamicMethod CreateGetDynamicMethod(Type type)
    {
        return new DynamicMethod("DynamicGet", typeof(object), 
              new Type[] { typeof(object) }, type, true);
    }

    // CreateSetDynamicMethod
    private static DynamicMethod CreateSetDynamicMethod(Type type)
    {
        return new DynamicMethod("DynamicSet", typeof(void), 
              new Type[] { typeof(object), typeof(object) }, type, true);
    }

    // BoxIfNeeded
    private static void BoxIfNeeded(Type type, ILGenerator generator)
    {
        if (type.IsValueType)
        {
            generator.Emit(OpCodes.Box, type);
        }
    }

    // UnboxIfNeeded
    private static void UnboxIfNeeded(Type type, ILGenerator generator)
    {
        if (type.IsValueType)
        {
            generator.Emit(OpCodes.Unbox_Any, type);
        }
    }
}

Each of these methods takes a Type and returns an appropriate delegate. This delegate then points to a chunk of dynamically generated IL, which can be invoked to perform the needed task. For example, GetHandler(myInstance) returns the value of the field/property that the handler was created for.

Unfortunately, I have a very limited understanding of IL, so I'm unable to give an in-depth explanation of what’s going on under the hood here. I had to do a fair amount of Googling, decompiling, and good old fashioned trial and error to write this code.

Here's what the delegate you get back looks like:

public delegate object GetHandler(object source);

This delegate takes one object as an argument, which is the object containing the property/field that you want to get the value of. The return is also an object and will be the value of the property/field on the object that was passed in. Here's an example of how to get the delegate.

Type type = typeof(<your class>);

FieldInfo fieldInfo = type.GetField(<field or property name>, BindingFlags.Instance | 
              BindingFlags.NonPublic | BindingFlags.Public);

GetHandler getHandler = DynamicMethodCompiler.CreateGetHandler(type, fieldInfo);

And here's an example of how to use the delegate once you have it.

MyClass myClass = new MyClass();
myClass.MyProperty = 4;
int currentValue = (int)getHandler(myClass);

In this example, currentValue will equal 4 at the end of the code.

Note that there is still some reflection involved. You still need to get a reference to the appropriate reflection object (e.g. FieldInfo, PropertyInfo). However, this is a one time cost. You only need to use reflection to create the Delegate and after that everything is compiled. This is a much lower cost than using reflection every time you want to get or set a value.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Herbrandson
Software Developer (Senior) Scratch Audio
United States United States
No Biography provided
Follow on   Twitter

Comments and Discussions

 
GeneralMy vote of 5 PinmemberHalil ibrahim Kalkan16-Jun-11 5:34 
GeneralIn real world application DynamicMethod are slower the Reflection Pinmemberedika200021-Aug-09 1:48 
HI,
 
I've made some test of your code into my ORM because I was having your same problem.
But after some test I've notice that this solution is slower then mine.
In a real application the call to SetHandler and GetHandler are made each time I need to get or set value. In your example the comparison with the reflection is done only beetween 'fieldInfo.GetValue(simpleClass)' and 'setHandler(simpleClass, "test")' but for a real comparison you need to put inside the loop cycle also the call to 'SetHandler setHandler = DynamicMethodCompiler.CreateSetHandler(type, fieldInfo)' because this is also a call you made each time you need to set/get value.
Testing a little bit the Reflection I've discovered that the real costs is the GetProperty, GetField methods not the SetValue or GetValue.
So my solution is to store in a static class that works like a cache, the PropertyInfo and FieldInfo of each businessobject of the ORM, then, when need to set/get value, acces the cache, get the PropertyInfo/FieldInfo for the requeste business object and set/get the value PropertyInfo/FieldInfo.
 
Change your test code in this and look at the result. The dynamic method call is really slower the Reflection.
 
// Main
static void Main(string[] args)
{
Console.WriteLine("How Many Test Iterations Would You Like To Run?");
int loops = int.Parse(Console.ReadLine());
 
CreateObjectUsingReflection(loops);
CreateObjectUsingDynamicMethodCall(loops);
 
SetValueUsingReflection(loops);
SetValueUsingReflectionWithCache(loops);
SetValueUsingDynamicMethodCall(loops);
 
GetValueUsingReflection(loops);
GetValueUsingReflectionWithCache(loops);
GetValueUsingDynamicMethodCall(loops);
 
Console.WriteLine("Test Complete.");
Console.Read();
}
 
// CreateObjectUsingReflection
private static void CreateObjectUsingReflection(int loops)
{
StartTest("Begin CreateObjectUsingReflection");
 
for (int i = 0; i < loops; i++)
{
object result = Activator.CreateInstance(type, true);
}
 
EndTest("End CreateObjectUsingReflection");
}
 
// CreateObjectUsingDynamicMethodCall
private static void CreateObjectUsingDynamicMethodCall(int loops)
{
StartTest("Begin CreateObjectUsingDynamicMethodCall");
 
InstantiateObjectHandler instantiateObjectHandler = DynamicMethodCompiler.CreateInstantiateObjectHandler(type);
 
for (int i = 0; i < loops; i++)
{
object result = instantiateObjectHandler();
}
 
EndTest("End CreateObjectUsingDynamicMethodCall");
}
 
// SetValueUsingReflection
private static void SetValueUsingReflection(int loops)
{
StartTest("Begin SetValueUsingReflection");
 
for (int i = 0; i < loops; i++)
{
fieldInfo = type.GetField("stringField", BINDING_FLAGS);
fieldInfo.SetValue(simpleClass, "test");
}
 
EndTest("End SetValueUsingReflection");
}
 
// SetValueUsingReflection
private static void SetValueUsingReflectionWithCache(int loops)
{
StartTest("Begin SetValueUsingReflectionWithCache");
 
for (int i = 0; i < loops; i++)
{
fieldInfo.SetValue(simpleClass, "test");
}
 
EndTest("End SetValueUsingReflection");
}
 
// SetValueUsingDynamicMethodCall
private static void SetValueUsingDynamicMethodCall(int loops)
{
StartTest("Begin SetValueUsingDynamicMethodCall");
 
for (int i = 0; i < loops; i++)
{
SetHandler setHandler = DynamicMethodCompiler.CreateSetHandler(type, fieldInfo);
 
setHandler(simpleClass, "test");
}
 
EndTest("End SetValueUsingDynamicMethodCall");
}
 
// GetValueUsingReflection
private static void GetValueUsingReflection(int loops)
{
StartTest("Begin GetValueUsingReflection");
 
for (int i = 0; i < loops; i++)
{
fieldInfo = type.GetField("stringField", BINDING_FLAGS);
string value = (string)fieldInfo.GetValue(simpleClass);
}
 
EndTest("End GetValueUsingReflection");
}
 
// GetValueUsingReflection
private static void GetValueUsingReflectionWithCache(int loops)
{
StartTest("Begin GetValueUsingReflectionWithCache");
 
for (int i = 0; i < loops; i++)
{
string value = (string)fieldInfo.GetValue(simpleClass);
}
 
EndTest("End GetValueUsingReflection");
}
 
// GetValueUsingDynamicMethodCall
private static void GetValueUsingDynamicMethodCall(int loops)
{
StartTest("Begin GetValueUsingDynamicMethodCall");
 
for (int i = 0; i < loops; i++)
{
GetHandler getHandler = DynamicMethodCompiler.CreateGetHandler(type, fieldInfo);
 
string value = (string)getHandler(simpleClass);
}
 
EndTest("End GetValueUsingDynamicMethodCall");
}
 
// StartTest
private static void StartTest(string message)
{
lastTestStartTime = DateTime.Now;
Console.WriteLine(message);
}
 
// EndTest
private static void EndTest(string message)
{
Console.WriteLine(message);
Console.WriteLine(DateTime.Now - lastTestStartTime);
Console.WriteLine("");
}
GeneralRe: In real world application DynamicMethod are slower the Reflection Pinmemberreborn_zhang29-Apr-10 0:00 
GeneralRe: In real world application DynamicMethod are slower the Reflection Pinmemberreborn_zhang1-Jun-10 2:13 
GeneralRe: In real world application DynamicMethod are slower the Reflection PinmemberHerbrandson1-Jun-10 5:19 
GeneralRe: In real world application DynamicMethod are slower the Reflection Pinmemberreborn_zhang1-Jun-10 5:31 
GeneralRe: In real world application DynamicMethod are slower the Reflection PinmemberHerbrandson1-Jun-10 5:55 
QuestionIdeas on converting the reflection of my code into Dynamic Code Generation [modified] PinmemberMember 227944716-Mar-09 3:46 
GeneralConsiderations when using Reflection.Emit PinmemberThoughthopper24-Oct-08 12:12 
QuestionWhat happens to the delegates in memory [modified] PinmemberThoughthopper24-Oct-08 11:04 
AnswerRe: What happens to the delegates in memory PinmemberHerbrandson27-Oct-08 6:13 
AnswerRe: What happens to the delegates in memory PinmemberIzzet Kerem Kusmezer31-Jan-09 8:44 
QuestionHow to call a function dynamically using DynamicMethod class PinmemberJyothiKumar G10-Oct-08 3:53 
AnswerRe: How to call a function dynamically using DynamicMethod class PinmemberHerbrandson10-Oct-08 6:17 
GeneralRe: How to call a function dynamically using DynamicMethod class PinmemberJyothiKumar G13-Oct-08 20:58 
GeneralRe: How to call a function dynamically using DynamicMethod class PinmemberHerbrandson14-Oct-08 6:04 
GeneralSuggested enhancements [modified] PinmemberK Robertson26-Aug-08 9:18 
QuestionGetConstructor() returns null on value types??? PinmemberE! Ray K20-Aug-08 12:41 
AnswerRe: GetConstructor() returns null on value types??? PinmemberK Robertson26-Aug-08 8:53 
GeneralRe: GetConstructor() returns null on value types??? Pinmembernrpog15-Sep-08 2:04 
GeneralHelp with DynamicMethod PinmemberDewey13-Apr-08 22:39 
GeneralRe: Help with DynamicMethod PinmemberHerbrandson14-Apr-08 7:41 
GeneralRe: Help with DynamicMethod PinmemberDewey14-Apr-08 12:04 
GeneralRe: Help with DynamicMethod PinmemberHerbrandson14-Apr-08 12:24 
GeneralRe: Help with DynamicMethod PinmemberDewey14-Apr-08 12:33 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.150224.1 | Last Updated 22 Jan 2007
Article Copyright 2006 by Herbrandson
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid