Search code examples
c#dynamicdefault-valuetypebuilder

FieldBuilder - Ho do you set the default value?


I'm using TypeBuilder to create types at runtime, but can't seem to figure out how you set the default value of a field.

For example, say I wanted to create the following layout at runtime, using a TypeBuilder, how do I set the default value of 'm_number' (to 64)?

    public class DynamicType1
{
    private int m_number = 64;

    public int Number
    {
        get { return m_number; }
        set { m_number = value; }
    }
}

Here is the code I use to create a property, ... how do I set the field to have the value of 'defaultValue'

void AddProperty(string name, Type type, object defaultValue)
    {
        // Add a private field of type 
        FieldBuilder fieldBuilder = m_typeBuilder.DefineField("m_" + name.ToLower(), type, FieldAttributes.Private);

        // Define a property that gets and sets the private field. The last argument of DefineProperty is null, because the 
        // property has no parameters. (If you don't specify null, you must specify an array of Type objects. For a parameterless property, 
        // use the built-in array with no elements: Type.EmptyTypes)
        PropertyBuilder propertyBuilder = m_typeBuilder.DefineProperty(name, PropertyAttributes.HasDefault, type, null);

        // The property "set" and property "get" methods require a special set of attributes.
        MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;

        // Define the "get" accessor method. The method returns the value and has no arguments. (Note that null could be  
        // used instead of Types.EmptyTypes)
        MethodBuilder getAccessorMethodBuilder = m_typeBuilder.DefineMethod("get_" + name, getSetAttr, type, Type.EmptyTypes);

        // For an instance property, argument zero is the instance. Load the instance, then load the private 
        //field and return, leaving the field value on the stack.
        ILGenerator numberGetIL = getAccessorMethodBuilder.GetILGenerator();
        numberGetIL.Emit(OpCodes.Ldarg_0);
        numberGetIL.Emit(OpCodes.Ldfld, fieldBuilder);
        numberGetIL.Emit(OpCodes.Ret);

        // Define the "set" accessor method for Number, which has no return type and takes one argument of type T.
        MethodBuilder setAccessorMethodBuilder = m_typeBuilder.DefineMethod("set_" + name, getSetAttr, null, new Type[] { type });

        // Load the instance and then the argument, then store the  argument in the field.
        ILGenerator numberSetIL = setAccessorMethodBuilder.GetILGenerator();
        numberSetIL.Emit(OpCodes.Ldarg_0);
        numberSetIL.Emit(OpCodes.Ldarg_1);
        numberSetIL.Emit(OpCodes.Stfld, fieldBuilder);
        numberSetIL.Emit(OpCodes.Ret);

        // Last, map the "get" and "set" accessor methods to the PropertyBuilder. The property is now complete. 
        propertyBuilder.SetGetMethod(getAccessorMethodBuilder);
        propertyBuilder.SetSetMethod(setAccessorMethodBuilder);
    }

Solution

  • When in doubt, ask Roslyn, in IL mode :-) http://goo.gl/NebcEP

    The default field is set in the constructor(s) of the object:

    .method public hidebysig specialname rtspecialname 
        instance void .ctor () cil managed 
    {
        // Method begins at RVA 0x2061
        // Code size 15 (0xf)
        .maxstack 8
    
        IL_0000: ldarg.0
        IL_0001: ldc.i4.s 64
        IL_0003: stfld int32 DynamicType1::m_number
    

    Note that if there are multiple constructors, it seems that constructors that chain to other constructors of the same class don't set the value, while constructors that don't chain to other constructor of the same class do set. See expanded example: http://goo.gl/8y90kU