Reflection Emit Derived by ObservableCollection

96 views Asked by At

I want to create dynamic type with refelction emit like:

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;
    }
}

The code I could not complete is this like:

 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");

Your help would be much appreciated!!

1

There are 1 answers

2
Andrey Tretyak On BEST ANSWER

Your solution has several problems:

  • AssemblyBuilderAccess should be RunAndSave to allow type instance create objects in run time.
  • You need to specify body for constructor.
  • In constructor body you should call base type (ObservableCollection) constructor.
  • In constructor body you should set field value from constructor parameters.

My solution for this problem with both constructors is something like this:

        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);