Dynamically create type and call constructor of base-class
Asked Answered
P

2

13

I need to create a class dynamically. Most things work fine but i'm stuck in generating the constructor.

AssemblyBuilder _assemblyBuilder =
        AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("MyBuilder"),                                                        AssemblyBuilderAccess.Run);

ModuleBuilder _moduleBuilder = _assemblyBuilder.DefineDynamicModule("MyModule");

public static object GetInstance<TSource, TEventArgs>(this TSource source, string eventName)
    where TSource : class
{
    var typeName = "MyTypeName";
    var typeBuilder = _moduleBuilder.DefineType(typeName, TypeAttributes.Class | TypeAttributes.Public);

    // create type like class MyClass : GenericType<MyClass, TSource, TEventArgs>
    var baseNotGenericType = typeof(GenericType<,,>);
    var baseType = baseNotGenericType.MakeGenericType(typeBuilder, typeof(TSource), typeof(TEventArgs)); 
    typeBuilder.SetParent(baseType);


    // the base class contains one constructor with string as param
    var baseCtor = baseNotGenericType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(string) }, null);

    var ctor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard | CallingConventions.HasThis, new Type[0]);
    var ilGenerator = ctor.GetILGenerator();

    // i want to call the constructor of the baseclass with eventName as param
    ilGenerator.Emit(OpCodes.Ldarg_0); // push "this"
    ilGenerator.Emit(OpCodes.Ldstr, eventName); // push the param
    ilGenerator.Emit(OpCodes.Call, baseCtor);
    ilGenerator.Emit(OpCodes.Ret);

    var type = typeBuilder.CreateType();

    // return ...
}

On call of the constructor i'm getting a BadImageFormatException. What am i doing wrong?

As requested:

The BaseClass looks something like this:

public abstract class GenericType<GT, TEventSource, TEventArgs> : BaseClass
    where GT: GenericType<GT, TEventSource, TEventArgs>, new()
    where TEventArgs : EventArgs
    where TEventSource : class
{
    protected GenericType(string eventName)
    {
        _eventName = eventName;
    }
    // ...
}

What i would like to have as a result in runtime:

public class MyType : BaseClass<MyType, ConcreteSourceType, ConcreteEventArgsType>
{
    protected MyType() : base("SomeName")
    {

    }
}
Pinnati answered 10/4, 2012 at 14:7 Comment(7)
Have a look here #893923Drummer
Not really helpful or did i miss something? I want to inherit from a baseclass and call it's base constructor which have an argument.Vocation
Try this link as well blogs.msdn.com/b/cburrows/archive/2009/04/22/…Drummer
This link is about a dynamic as baseclass. I need a dynamically generated subclass from a predefines generic baseclass.Vocation
How do the generic parameters work here? Is this code in a class (or method) that has generic parameters TSource and TSource?Kayseri
I edit the question to answer your question. I let out the return statement because i am accessing some static property and so on; just to keep it simple.Vocation
please paste the desired class definition also in regular C# to make the question clearer (i.e. what the class would look like if you defined it declaratively.)Drew
K
13

I think the problem is that you're trying to call the constructor of the open generic type GenericType<GT, TEventSource, TEventArgs>, but you need to call the constructor of the closed type BaseClass<MyType, ConcreteSourceType, ConcreteEventArgsType>. The solution to the seems simple:

var baseCtor = baseType.GetConstructor(
    BindingFlags.NonPublic | BindingFlags.Instance, null,
    new[] { typeof(string) }, null);

The problem is that this doesn't work and throws NotSupportedException. So, it seems getting the constructor of a generic type, where one of the parameters is a TypeBuilder is nut supported.

Because of that, I think what you want is not possible using Reflection.Emit, unless there is some hack to work around this.

EDIT: A-HA! I had to dive deep into Reflection.Emit in Reflector (although looking at the right place in the documentation would have worked too), but I found it: there is a special method exactly for this: the static TypeBuilder.GetConstructor(). So this should work:

var baseNonGenericCtor = baseNotGenericType.GetConstructor(
    BindingFlags.NonPublic | BindingFlags.Instance, null,
    new[] { typeof(string) }, null);
var baseCtor = TypeBuilder.GetConstructor(baseType, baseNonGenericCtor);
Kayseri answered 10/4, 2012 at 15:19 Comment(6)
So i am looking forward to see a hack :)Vocation
Actually, it is possible and it's not a hack at all, see edit.Kayseri
BTW, questions like this that make me learn something new are the best.Kayseri
No credit to me for suggesting reflection.emit language in reflector?Drew
How do you activate and get your instance from this?Skeet
@Skeet Call the Invoke() method.Kayseri
D
5

The easiest way to do this would be to compile up your abstract and derived classes into a simple assembly, then open them in Reflector using the "Reflection.Emit" language available as a addin from:

http://reflectoraddins.codeplex.com/

Reflector: Reflection.Emit language

Yes, that's as cool as it sounds :)

Drew answered 10/4, 2012 at 15:27 Comment(4)
If your desired class definition is valid c# and will compile, then yes.Drew
Well, the code it generates in this case doesn't make much sense and wouldn't help the OP. That's because it uses typeof(MyType), which doesn't make sense when you're building that type. And using typeBuilder instead would lead to a NotSupportedException.Kayseri
Good point... I see that now. I guess recursive parametric types are going to be tough. :)Drew
Sorry for late "credit" :) I'm still trying if it really works. But thanks for the idea!Vocation

© 2022 - 2024 — McMap. All rights reserved.