views:

203

answers:

2

Dear ladies and sirs.

Imagine the following perfectly legal type hierarchy:

class A<T> where T : A<T>
{
}

class B : A<B>
{
  public B():base(){}
}

My question is given a statically compiled definition of A<> is it possible to emit the type B dynamically?

The problem is how to specify the parent type in ModuleBuilder.DefineType.

Or maybe there is another way to produce such a type, other than

  • using the aforementioned method
  • using CodeDom (which is much like creating a temporary file and passing it to csc.exe :-))

EDIT: The type B should have explicit public default constructor invoking the default constructor inherited from A<B>.

A: 

Take a look at this question. I think you have the same problem.

Ronald Wildenberg
Thanks for the link. Using CodeDom is pretty much the same as the second unwanted option I have mentioned in the question - just dump the source to a temporary source file and invoke csc.exe. I should probably rephrase my question.
mark
+1  A: 

You can use an overload of ModuleBuilder.DefineType which doesn't specify a parent type, and then use the TypeBuilder.SetParent method to set the parent to the recursive type (using an argument something like typeof(A<>).MakeGenericType(tb) where tb is your TypeBuilder, but I don't have a C# compiler in front of me).

EDIT - here's a working example, assuming you've got a ModuleBuilder mb. For an empty default constructor, you don't need to use the DefineConstructor method at all; alternatively you could use DefineDefaultConstructor. I've included an example where the base constructor is explicitly called, though, in case you have some extra logic you want to add in there.

TypeBuilder tb = mb.DefineType("B");
Type AB = typeof(A<>).MakeGenericType(tb);
tb.SetParent(AB);
ConstructorInfo ctor = TypeBuilder.GetConstructor(AB, typeof(A<>).GetConstructor(new Type[] { }));
ILGenerator ilg = tb.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[] { }).GetILGenerator();
ilg.Emit(OpCodes.Ldarg_0);
ilg.Emit(OpCodes.Call, ctor);
ilg.Emit(OpCodes.Ret);
Type t = tb.CreateType();
kvb
There is still a problem. Try generating the default constructor, which, of course, should invoke the base type default constructor.
mark
Don't Define(Default)Constructor methods work?
Anton Tykhyy
DefineConstructor returns ConstructorBuilder, which is used to emit the constructor's code. One of the code statements should invoke the base type's constructor, smth like this - ilg.Emit(OpCodes.Call, ctor); where ctor is the base type ConstructorInfo instance corresponding to the base type's constructor. The problem is how to obtain ctor? I failed.
mark
After you have set the generic parent type with `SetParent`, try `tb.BaseType.GetConstructor()` to obtain the base type's constructor.
Anton Tykhyy
Have you tried it? Because I have and it fails. Go ahead, see for yourself.
mark
Well, the default constructor will be created by default anyway (no action is required on your part). Calling DefineDefaultConstructor explicitly also works for me...
kvb
To get a constructor that you can call explicitly from within your ILGenerator, you can use something along the lines of `var ctor = TypeBuilder.GetConstructor(typeof(A<>).MakeGenericType(tb), typeof(A<>).GetConstructor(new object[] {}));`.
kvb
Thanks folks. I was unaware of the static TypeBuilder.GetConstructor method.
mark