Click here to Skip to main content
Licence CPOL
First Posted 28 Jul 2006
Views 123,016
Downloads 760
Bookmarked 126 times

Dynamic Code Generation vs Reflection

By | 22 Jan 2007 | Article
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)

About the Author

Herbrandson

Software Developer (Senior)
Scratch Audio
United States United States

Member

Follow on Twitter Follow on Twitter


Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
GeneralMy vote of 5 PinmemberHalil ibrahim Kalkan4:34 16 Jun '11  
GeneralIn real world application DynamicMethod are slower the Reflection Pinmemberedika20000:48 21 Aug '09  
GeneralRe: In real world application DynamicMethod are slower the Reflection Pinmemberreborn_zhang23:00 28 Apr '10  
GeneralRe: In real world application DynamicMethod are slower the Reflection Pinmemberreborn_zhang1:13 1 Jun '10  
GeneralRe: In real world application DynamicMethod are slower the Reflection PinmemberHerbrandson4:19 1 Jun '10  
GeneralRe: In real world application DynamicMethod are slower the Reflection Pinmemberreborn_zhang4:31 1 Jun '10  
GeneralRe: In real world application DynamicMethod are slower the Reflection PinmemberHerbrandson4:55 1 Jun '10  
QuestionIdeas on converting the reflection of my code into Dynamic Code Generation [modified] PinmemberMember 22794472:46 16 Mar '09  
GeneralConsiderations when using Reflection.Emit PinmemberThoughthopper11:12 24 Oct '08  
QuestionWhat happens to the delegates in memory [modified] PinmemberThoughthopper10:04 24 Oct '08  
AnswerRe: What happens to the delegates in memory PinmemberHerbrandson5:13 27 Oct '08  
AnswerRe: What happens to the delegates in memory PinmemberIzzet Kerem Kusmezer7:44 31 Jan '09  
QuestionHow to call a function dynamically using DynamicMethod class PinmemberJyothiKumar G2:53 10 Oct '08  
AnswerRe: How to call a function dynamically using DynamicMethod class PinmemberHerbrandson5:17 10 Oct '08  
GeneralRe: How to call a function dynamically using DynamicMethod class PinmemberJyothiKumar G19:58 13 Oct '08  
GeneralRe: How to call a function dynamically using DynamicMethod class PinmemberHerbrandson5:04 14 Oct '08  
GeneralSuggested enhancements [modified] PinmemberK Robertson8:18 26 Aug '08  
QuestionGetConstructor() returns null on value types??? PinmemberE! Ray K11:41 20 Aug '08  
AnswerRe: GetConstructor() returns null on value types??? PinmemberK Robertson7:53 26 Aug '08  
GeneralRe: GetConstructor() returns null on value types??? Pinmembernrpog1:04 15 Sep '08  
GeneralHelp with DynamicMethod PinmemberDewey21:39 13 Apr '08  
GeneralRe: Help with DynamicMethod PinmemberHerbrandson6:41 14 Apr '08  
GeneralRe: Help with DynamicMethod PinmemberDewey11:04 14 Apr '08  
I'm using the code from this article exactly as you have it, but I'm using Microsoft's SilverLight 2.0 Beta 1 as the client, and for security reasons(of which I'm unsure), they don't allow the other constructors.
 
I don't know how possible it is to make your code work without the other flags, but I thought you would be the first person to ask. I don't mean to have you do my work for me, but I'm not sure how the lack of these parameters affect the IL code, and any type changing that may be needed.
 
Thanks.
GeneralRe: Help with DynamicMethod PinmemberHerbrandson11:24 14 Apr '08  
GeneralRe: Help with DynamicMethod PinmemberDewey11:33 14 Apr '08  

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.

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