Getting the type of a class created during runtime using TypeBuilder

1.3k views Asked by At

I have the following block of code where I am building a class (SampleModel) using TypeBuilder. Once the type has been created, I'm trying to use Type.GetType to get the Type of the class I just created. But Type.GetType is returning null. Why is this?

namespace TypeBuilderTest
{
    using System;
    using System.Reflection;
    using System.Reflection.Emit;

    class Program
    {
        static void Main()
        {
            // create a dynamic assembly and module 
            var assemblyName = new AssemblyName("SampleModelAssembly");
            var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);

            var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name);

            var runtimeModelType = CreateRuntimeModel(moduleBuilder);

            var type = Type.GetType(runtimeModelType.AssemblyQualifiedName); // <= This is the call in question.
            Console.WriteLine("Type: " + type);
        }

        static private Type CreateRuntimeModel(ModuleBuilder moduleBuilder)
        {
            var modelName = "SampleModel";

            // create a new type builder
            var typeBuilder = moduleBuilder.DefineType(
                modelName,
                TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Serializable);

            AddProperty(typeBuilder, "SampleAttribute", typeof(string));

            return typeBuilder.CreateType();
        }

        static public void AddProperty(TypeBuilder typeBuilder, string propertyName, Type propertyType)
        {
            // Generate a private field
            FieldBuilder field = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);

            // Generate a public property
            //
            // 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 property =
                typeBuilder.DefineProperty(propertyName,
                                 System.Reflection.PropertyAttributes.None,
                                 propertyType,
                                 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 for current private field.
            MethodBuilder currGetPropMthdBldr =
                typeBuilder.DefineMethod("get_value",
                                           GetSetAttr,
                                           propertyType,
                                           Type.EmptyTypes);

            ILGenerator currGetIL = currGetPropMthdBldr.GetILGenerator();
            // 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.
            currGetIL.Emit(OpCodes.Ldarg_0);
            currGetIL.Emit(OpCodes.Ldfld, field);
            currGetIL.Emit(OpCodes.Ret);

            // Define the "set" accessor method for current private field.
            MethodBuilder currSetPropMthdBldr =
                typeBuilder.DefineMethod("set_value",
                                           GetSetAttr,
                                           null,
                                           new Type[] { propertyType });

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

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

runtimeModelType.AssemblyQualifiedName shows SampleModel, SampleModelAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null

P.S: When I run the same program via LinqPad, intermittently I get the type back. Rest of the time, I get null there too.

1

There are 1 answers

0
Joe Albahari On BEST ANSWER

Get type type by calling

assemblyBuilder.GetType (runtimeModelType.FullName)

Calling Type.GetType relies on fusion to locate the correct assembly, which I suspect could be unreliable because it's dynamically created. At a minimum, it adds a layer of unnecessary complexity.

LINQPad has an additional hook to help the runtime find assemblies, and this might be kicking in to make it (sometimes) work.