由 ObservableCollection 派生的反射发射

Reflection Emit Derived by ObservableCollection

我想创建带有 refelction emit 的动态类型,例如:

public class ObservableTestColleciton<T> : ObservableCollection<T>
{
    public T Parent { get; set; }
    public ObservableTestColleciton(T parent)
    {
        Parent = parent;
    }
    public ObservableTestColleciton(T parent, IEnumerable<T> source):base(source)
    {
        Parent = parent;
    }
}

我无法完成的代码是这样的:

 AppDomain myDomain = AppDomain.CurrentDomain;
    AssemblyName myAsmName = new AssemblyName("AAB");
    AssemblyBuilder myAssembly =      myDomain.DefineDynamicAssembly(myAsmName,AssemblyBuilderAccess.Save);
    ModuleBuilder myModule = myAssembly.DefineDynamicModule(myAsmName.Name,myAsmName.Name + ".dll");
    TypeBuilder myType = myModule.DefineType("ObservableTestCollection", TypeAttributes.Class | TypeAttributes.Public);

    string[] typeParamNames = { "T" };
    GenericTypeParameterBuilder[] typeParams = myType.DefineGenericParameters(typeParamNames);

    Type observableOf = typeof(ObservableCollection<>);
    Type genOb = observableOf.MakeGenericType(typeParams[0]);          
    FieldBuilder myField = myType.DefineField("Parent", typeParams[0], FieldAttributes.Public);
    ConstructorBuilder constructor = myType.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, Type.EmptyTypes);         

    var type = myType.CreateType();
    var obj = Activator.CreateInstance(type);
    myAssembly.Save("AAB.dll");

非常感谢您的帮助!!

您的解决方案存在几个问题:

  • AssemblyBuilderAccess 应该是 RunAndSave 以允许类型实例在 运行 时间内创建对象。
  • 您需要为构造函数指定主体。
  • 在构造函数主体中,您应该调用基类型 (ObservableCollection) 构造函数。
  • 在构造函数主体中,您应该从构造函数参数中设置字段值。

我对两个构造函数的这个问题的解决方案是这样的:

        const string typeName = "ObservableTestCollection";
        const string fieldName = "Parent";
        const string assemblyName = "TestAssembly";
        const string assemblyFileName = assemblyName + ".dll";

        var domain = AppDomain.CurrentDomain;
        var assemblyBuilder = domain.DefineDynamicAssembly(new AssemblyName(assemblyName), AssemblyBuilderAccess.RunAndSave);
        var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName, assemblyFileName);

        var baseType = typeof(ObservableCollection<>);
        var typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Class | TypeAttributes.Public, baseType);
        var genericParameters = typeBuilder.DefineGenericParameters("T");
        var genericParameter = genericParameters.First();

        var fieldBuilder = typeBuilder.DefineField(fieldName, genericParameter, FieldAttributes.Public);

        //First constructor ObservableTestColleciton(T parent)
        var ctorParameters = new Type[] { genericParameter };
        var baseCtor = baseType.GetConstructor(Type.EmptyTypes);
        var ctorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, ctorParameters);
        var generator = ctorBuilder.GetILGenerator();
        generator.Emit(OpCodes.Ldarg_0); // load this
        generator.Emit(OpCodes.Call, baseCtor); //call base constructor
        generator.Emit(OpCodes.Ldarg_0); // load this
        generator.Emit(OpCodes.Ldarg_1); // load argument value
        generator.Emit(OpCodes.Stfld, fieldBuilder); // store into Parent field
        generator.Emit(OpCodes.Ret); //return

        //Second constructor ObservableTestColleciton(T parent, IEnumerable<T> source):base(source)
        var baseCtorParam = typeof(IEnumerable<>).MakeGenericType(genericParameter);
        ctorParameters = new [] { genericParameter, baseCtorParam };
        baseCtor = baseType.GetConstructors()
                           .First(c => c.GetParameters().FirstOrDefault()?.ParameterType?.GetGenericTypeDefinition() == typeof(IEnumerable<>));

        ctorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, ctorParameters);
        generator = ctorBuilder.GetILGenerator();
        generator.Emit(OpCodes.Ldarg_0); // load this
        generator.Emit(OpCodes.Ldarg_2); // load second argument value
        generator.Emit(OpCodes.Call, baseCtor); //call base constructor
        generator.Emit(OpCodes.Ldarg_0); // load this
        generator.Emit(OpCodes.Ldarg_1); // load first argument value
        generator.Emit(OpCodes.Stfld, fieldBuilder); // store into Parent field
        generator.Emit(OpCodes.Ret); //return

        var genericType = typeBuilder.CreateType();
        var type = genericType.MakeGenericType(typeof(string));
        var fieldInfo = type.GetField(fieldName);
        var obj1 = Activator.CreateInstance(type, "Parent1");
        Console.WriteLine("Ctor1 field value :" + fieldInfo.GetValue(obj1)); //check that field value was set
        var obj2 = Activator.CreateInstance(type, "Parent2", new List<string>());
        Console.WriteLine("Ctor1 field value :" + fieldInfo.GetValue(obj2));
        assemblyBuilder.Save(assemblyFileName);