使用 TypeBuilder 获取在运行时创建的 class 的类型

Getting the type of a class created during runtime using TypeBuilder

我有以下代码块,我正在使用 TypeBuilder 构建 class (SampleModel)。创建类型后,我将尝试使用 Type.GetType 获取我刚刚创建的 class 的 Type。但是 Type.GetType 正在返回 null。这是为什么?

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 显示 SampleModel, SampleModelAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null

P.S:当我通过 LinqPad 运行 执行相同的程序时,间歇性地返回类型。其余时间,我也在那里得到 null 。

通过调用

获取类型类型
assemblyBuilder.GetType (runtimeModelType.FullName)

调用 Type.GetType 依靠融合来定位正确的程序集,我怀疑它可能不可靠,因为它是动态创建的。至少,它增加了一层不必要的复杂性。

LINQPad 有一个额外的钩子来帮助运行时查找程序集,这可能会使其(有时)起作用。