Friday, February 08, 2008

Creating Code at Runtime / System.Reflection.Emit

Here an example how we can create code at runtime. In this sample, I try to create the following class at runtime:

public class Person
{
    private string _firstname;
    private string _lastname;

    public string FirstName
    {
        get { return _firstname; }
        set { _firstname = value; }
    }

    public string LastName
    {
        get { return _lastname; }
        set { _lastname = value; }
    }

    public Person()
    {
        _firstname = "";
        _lastname = "";
    }

    public Person(string firstname, string lastname)
    {
        FirstName = firstname;
        LastName = lastname;
    }

    public string GetFullName()
    {
        return FirstName + " " + LastName;
    }
}
First of all, I'm going to implement the class as simple as posible, avoiding delegates an events. Here the result: 1.
// Our intermediate language generator
ILGenerator ilgen;

// The assembly builder
AssemblyBuilder asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("PeopleLibrary"), AssemblyBuilderAccess.RunAndSave);

// The module builder
ModuleBuilder modBuilder = asmBuilder.DefineDynamicModule("PeopleLibrary", "PeopleLibrary.dll");
2.
// The person class builder
TypeBuilder personBuilder = modBuilder.DefineType("PeopleLibrary.Person", TypeAttributes.Class | TypeAttributes.Public);

// The default constructor
ConstructorBuilder ctorBuilder = personBuilder.DefineDefaultConstructor(MethodAttributes.Public);
3.
// Two fields: m_firstname, m_lastname
FieldBuilder fBuilderFirstName = personBuilder.DefineField("m_firstname", typeof(string), FieldAttributes.Private);
FieldBuilder fBuilderLastName = personBuilder.DefineField("m_lastname", typeof(string), FieldAttributes.Private);

// Two properties for this object: FirstName, LastName
PropertyBuilder pBuilderFirstName = personBuilder.DefineProperty("FirstName", System.Reflection.PropertyAttributes.HasDefault, typeof(string), null);
PropertyBuilder pBuilderLastName = personBuilder.DefineProperty("LastName", System.Reflection.PropertyAttributes.HasDefault, typeof(string), null);
4.
// Custom attributes for get, set accessors
MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName;

// get,set accessors for FirstName
MethodBuilder mGetFirstNameBuilder = personBuilder.DefineMethod("get_FirstName", getSetAttr, typeof(string), Type.EmptyTypes);

// Code generation
ilgen = mGetFirstNameBuilder.GetILGenerator();
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Ldfld, fBuilderFirstName); // returning the firstname field
ilgen.Emit(OpCodes.Ret);

MethodBuilder mSetFirstNameBuilder = personBuilder.DefineMethod("set_FirstName", getSetAttr, null, new Type[] { typeof(string) });

// Code generation
ilgen = mSetFirstNameBuilder.GetILGenerator();
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Ldarg_1);
ilgen.Emit(OpCodes.Stfld, fBuilderFirstName); // setting the firstname field from the first argument (1)
ilgen.Emit(OpCodes.Ret);
5.
// get,set accessors for LastName
MethodBuilder mGetLastNameBuilder = personBuilder.DefineMethod("get_LastName", getSetAttr, typeof(string), Type.EmptyTypes);

// Code generation
ilgen = mGetLastNameBuilder.GetILGenerator();
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Ldfld, fBuilderLastName); // returning the firstname field
ilgen.Emit(OpCodes.Ret);

MethodBuilder mSetLastNameBuilder = personBuilder.DefineMethod("set_LastName", getSetAttr, null, new Type[] { typeof(string) });

// Code generation
ilgen = mSetLastNameBuilder.GetILGenerator();
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Ldarg_1);
ilgen.Emit(OpCodes.Stfld, fBuilderLastName); // setting the firstname field from the first argument (1)
ilgen.Emit(OpCodes.Ret);
6.
// Assigning get/set accessors
pBuilderFirstName.SetGetMethod(mGetFirstNameBuilder);
pBuilderFirstName.SetSetMethod(mSetFirstNameBuilder);

pBuilderLastName.SetGetMethod(mGetLastNameBuilder);
pBuilderLastName.SetSetMethod(mSetLastNameBuilder);
7.
// Now, a custom method named GetFullName that concatenates FirstName and LastName properties
MethodBuilder mGetFullNameBuilder = personBuilder.DefineMethod("GetFullName", MethodAttributes.Public, typeof(string), Type.EmptyTypes);

// Code generation
ilgen = mGetFullNameBuilder.GetILGenerator();
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Call, mGetFirstNameBuilder); // getting the firstname
ilgen.Emit(OpCodes.Ldstr, " "); // an space
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Call, mGetLastNameBuilder); // getting the lastname

// We need the 'Concat' method from string type
MethodInfo concatMethod = typeof(String).GetMethod("Concat", new Type[] { typeof(string), typeof(string), typeof(string) });

ilgen.Emit(OpCodes.Call, concatMethod); // calling concat and returning the result
ilgen.Emit(OpCodes.Ret);
8.
// Another constructor that initializes firstname and lastname
ConstructorBuilder ctorBuilder2 = personBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[] { typeof(string), typeof(string) });
ctorBuilder2.DefineParameter(1, ParameterAttributes.In, "firstname");
ctorBuilder2.DefineParameter(2, ParameterAttributes.In, "lastname");

// Code generation
ilgen = ctorBuilder2.GetILGenerator();

// First of all, we need to call the base constructor, 
// the Object's constructor in this sample
Type objType = Type.GetType("System.Object");
ConstructorInfo objCtor = objType.GetConstructor(Type.EmptyTypes);

ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Call, objCtor); // calling the Object's constructor

ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Ldarg_1);
ilgen.Emit(OpCodes.Call, mSetFirstNameBuilder); // setting the firstname field from the first argument (1)
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Ldarg_2);
ilgen.Emit(OpCodes.Call, mSetLastNameBuilder);  // setting the lastname field from the second argument (2)
ilgen.Emit(OpCodes.Ret);
9.
// Finally, create the type and save the assembly
personBuilder.CreateType();

asmBuilder.Save("PeopleLibrary.dll");
10. Using the library in another project that refereces PeopleLibrary.dll:
Person p = new Person("oscar", "londono");

MessageBox.Show(p.GetFullName());
See Part 2 and Part 3.

5 comments:

Eng. Sherif Mosad Abd Elfatah said...

Thank you very much, you did a magnificent work.

Keep on...

Eng. Sherif Mosad
Software Engineer
NextWare Inc., Egypt

Daniel said...

The best example on System.Reflection.Emit that I've found on the net and exactly what I need!

Thank you very much for your great BLOG!

Greetings from Germany,

Daniel

Gregory said...

Nice one as already stated the best on the net and believe me i searched a lot regarding this subject. Thanks a lot

SK MOHAMMAD ALI said...
This comment has been removed by the author.
Bryan Pampola - Dev PH said...

Hi Oscar,

Your work was great! Now it resolve my problems in creating class..

But I still have question? how about setting a property of the field? Which I only want is a SET property.

Please help again.. kindly appreciate it.


Thank you very much! :D


View My Stats