Click here to Skip to main content
15,860,859 members
Articles / Programming Languages / C#
Article

Dynamic Code Generation vs Reflection

Rate me:
Please Sign up or sign in to vote.
4.69/5 (39 votes)
22 Jan 2007CPOL3 min read 222.8K   1.6K   140   42
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).

C#
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:

C#
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.

C#
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.

C#
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)


Written By
Software Developer (Senior) Scratch Audio
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Halil ibrahim Kalkan16-Jun-11 4:34
Halil ibrahim Kalkan16-Jun-11 4:34 
GeneralIn real world application DynamicMethod are slower the Reflection Pin
edika9921-Aug-09 0:48
edika9921-Aug-09 0:48 
GeneralRe: In real world application DynamicMethod are slower the Reflection Pin
reborn_zhang28-Apr-10 23:00
reborn_zhang28-Apr-10 23:00 
GeneralRe: In real world application DynamicMethod are slower the Reflection Pin
reborn_zhang1-Jun-10 1:13
reborn_zhang1-Jun-10 1:13 
GeneralRe: In real world application DynamicMethod are slower the Reflection Pin
Herbrandson1-Jun-10 4:19
Herbrandson1-Jun-10 4:19 
GeneralRe: In real world application DynamicMethod are slower the Reflection Pin
reborn_zhang1-Jun-10 4:31
reborn_zhang1-Jun-10 4:31 
GeneralRe: In real world application DynamicMethod are slower the Reflection Pin
Herbrandson1-Jun-10 4:55
Herbrandson1-Jun-10 4:55 
QuestionIdeas on converting the reflection of my code into Dynamic Code Generation [modified] Pin
Rodelio Rodriguez16-Mar-09 2:46
Rodelio Rodriguez16-Mar-09 2:46 
GeneralConsiderations when using Reflection.Emit Pin
Thoughthopper24-Oct-08 11:12
Thoughthopper24-Oct-08 11:12 
QuestionWhat happens to the delegates in memory [modified] Pin
Thoughthopper24-Oct-08 10:04
Thoughthopper24-Oct-08 10:04 
AnswerRe: What happens to the delegates in memory Pin
Herbrandson27-Oct-08 5:13
Herbrandson27-Oct-08 5:13 
AnswerRe: What happens to the delegates in memory Pin
Izzet Kerem Kusmezer31-Jan-09 7:44
Izzet Kerem Kusmezer31-Jan-09 7:44 
QuestionHow to call a function dynamically using DynamicMethod class Pin
JyothiKumar G10-Oct-08 2:53
JyothiKumar G10-Oct-08 2:53 
AnswerRe: How to call a function dynamically using DynamicMethod class Pin
Herbrandson10-Oct-08 5:17
Herbrandson10-Oct-08 5:17 
GeneralRe: How to call a function dynamically using DynamicMethod class Pin
JyothiKumar G13-Oct-08 19:58
JyothiKumar G13-Oct-08 19:58 
GeneralRe: How to call a function dynamically using DynamicMethod class Pin
Herbrandson14-Oct-08 5:04
Herbrandson14-Oct-08 5:04 
GeneralSuggested enhancements [modified] Pin
Keith L Robertson26-Aug-08 8:18
Keith L Robertson26-Aug-08 8:18 
QuestionGetConstructor() returns null on value types??? Pin
E! Ray K20-Aug-08 11:41
E! Ray K20-Aug-08 11:41 
AnswerRe: GetConstructor() returns null on value types??? Pin
Keith L Robertson26-Aug-08 7:53
Keith L Robertson26-Aug-08 7:53 
GeneralRe: GetConstructor() returns null on value types??? Pin
nrpog15-Sep-08 1:04
nrpog15-Sep-08 1:04 
GeneralHelp with DynamicMethod Pin
Dewey13-Apr-08 21:39
Dewey13-Apr-08 21:39 
GeneralRe: Help with DynamicMethod Pin
Herbrandson14-Apr-08 6:41
Herbrandson14-Apr-08 6:41 
GeneralRe: Help with DynamicMethod Pin
Dewey14-Apr-08 11:04
Dewey14-Apr-08 11:04 
GeneralRe: Help with DynamicMethod Pin
Herbrandson14-Apr-08 11:24
Herbrandson14-Apr-08 11:24 
GeneralRe: Help with DynamicMethod Pin
Dewey14-Apr-08 11:33
Dewey14-Apr-08 11:33 

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

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