Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to emit a type deriving from a generic type while specifying itself as the generic type parameter?

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>.

like image 609
mark Avatar asked Sep 14 '09 12:09

mark


1 Answers

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();
like image 172
kvb Avatar answered Sep 21 '22 19:09

kvb