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 4:34 
GeneralIn real world application DynamicMethod are slower the Reflection Pinmemberedika200021-Aug-09 0:48 
GeneralRe: In real world application DynamicMethod are slower the Reflection Pinmemberreborn_zhang28-Apr-10 23:00 
GeneralRe: In real world application DynamicMethod are slower the Reflection Pinmemberreborn_zhang1-Jun-10 1:13 
GeneralRe: In real world application DynamicMethod are slower the Reflection PinmemberHerbrandson1-Jun-10 4:19 
GeneralRe: In real world application DynamicMethod are slower the Reflection Pinmemberreborn_zhang1-Jun-10 4:31 
GeneralRe: In real world application DynamicMethod are slower the Reflection PinmemberHerbrandson1-Jun-10 4:55 
QuestionIdeas on converting the reflection of my code into Dynamic Code Generation [modified] PinmemberMember 227944716-Mar-09 2:46 
GeneralConsiderations when using Reflection.Emit PinmemberThoughthopper24-Oct-08 11:12 
QuestionWhat happens to the delegates in memory [modified] PinmemberThoughthopper24-Oct-08 10:04 
AnswerRe: What happens to the delegates in memory PinmemberHerbrandson27-Oct-08 5:13 
AnswerRe: What happens to the delegates in memory PinmemberIzzet Kerem Kusmezer31-Jan-09 7:44 
QuestionHow to call a function dynamically using DynamicMethod class PinmemberJyothiKumar G10-Oct-08 2:53 
AnswerRe: How to call a function dynamically using DynamicMethod class PinmemberHerbrandson10-Oct-08 5:17 
GeneralRe: How to call a function dynamically using DynamicMethod class PinmemberJyothiKumar G13-Oct-08 19:58 
GeneralRe: How to call a function dynamically using DynamicMethod class PinmemberHerbrandson14-Oct-08 5:04 
GeneralSuggested enhancements [modified] PinmemberK Robertson26-Aug-08 8:18 
QuestionGetConstructor() returns null on value types??? PinmemberE! Ray K20-Aug-08 11:41 
AnswerRe: GetConstructor() returns null on value types??? PinmemberK Robertson26-Aug-08 7:53 
GeneralRe: GetConstructor() returns null on value types??? Pinmembernrpog15-Sep-08 1:04 
GeneralHelp with DynamicMethod PinmemberDewey13-Apr-08 21:39 
GeneralRe: Help with DynamicMethod PinmemberHerbrandson14-Apr-08 6:41 
GeneralRe: Help with DynamicMethod PinmemberDewey14-Apr-08 11:04 
GeneralRe: Help with DynamicMethod PinmemberHerbrandson14-Apr-08 11:24 
GeneralRe: Help with DynamicMethod PinmemberDewey14-Apr-08 11:33 
GeneralRe: Help with DynamicMethod PinmemberDewey14-Apr-08 11:36 
GeneralRe: Help with DynamicMethod PinmemberHerbrandson22-Apr-08 19:27 
GeneralRe: Help with DynamicMethod PinmemberDewey14-Apr-08 11:17 
General.NET 2.0 suggestions PinmemberSteve Hansen22-Jan-07 21:37 
GeneralRe: .NET 2.0 suggestions PinmemberHerbrandson23-Jan-07 5:03 
GeneralRe: .NET 2.0 suggestions PinmemberJames Curran23-Jan-07 16:25 
GeneralRe: .NET 2.0 suggestions PinmemberHerbrandson30-Jan-07 15:58 
GeneralRe: .NET 2.0 suggestions PinmemberK Robertson26-Aug-08 8:04 
GeneralRe: .NET 2.0 suggestions PinmemberJames Curran26-Aug-08 13:55 
GeneralRe: .NET 2.0 suggestions PinmemberK Robertson27-Aug-08 11:42 
GeneralWrapper Class Pinmemberjdmartinez5-Sep-06 5:44 
Generalthe most useful post I've seen Pinmemberdave.dolan14-Aug-06 19:45 
GeneralGoods sample PinmemberISharp1-Aug-06 16:11 
AnswerRe: Goods sample PinmemberHerbrandson1-Aug-06 19:06 
Generalmore examples Pinmemberpointnetsolutions28-Jul-06 12:05 
GeneralRe: more examples PinmemberHerbrandson2-Aug-06 6:22 
GeneralVery Nice! PinmemberJohnDeHope328-Jul-06 8:14 

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 | Mobile
Web01 | 2.8.141015.1 | Last Updated 22 Jan 2007
Article Copyright 2006 by Herbrandson
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid