Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create type "MyClass : OtherClass<MyClass> { }" at runtime?

Is it possible in C# to create a type at runtime that inherits from a generic class where the template parameter for the base class is the current class being constructed? This will compile fine:

// I have this class:
public class OtherClass<T>
    where T : OtherClass<T>
{ }

// I want to create this at runtime:
public class MyClass : OtherClass<MyClass>
{ }

but I'm not sure how to create the MyClass using System.Reflection.Emit.ModuleBuilder.TypeBuilder:

AssemblyName asn = new AssemblyName("test.dll");
AssemblyBuilder asb = AppDomain.CurrentDomain.DefineDynamicAssembly(
    asn, AssemblyBuilderAccess.RunAndSave, @"D:\test_assemblies");

ModuleBuilder = modb = asb.DefineDynamicModule("test", "test.dll");

TypeBuilder tb = modb.DefineType(
    "test",
    TypeAttributes.Public | TypeAttributes.Class,
    typeof(MyClass)); // How to specify inheritance?

// populate properties, fields, methods, etc., emit...

tb.CreateType();

Is this possible?

Edit - Based on replies so far, I've tried this:

public class OtherClass<T>
    where T : OtherClass<T>
{ }

public static void CreateSimple()
{
    AssemblyName asn = new AssemblyName("test");
    AssemblyBuilder asb = AppDomain.CurrentDomain.DefineDynamicAssembly(
        asn, AssemblyBuilderAccess.RunAndSave, @"D:\test_asms");
    ModuleBuilder modb = asb.DefineDynamicModule("test", "test.dll");

    try
    {
        TypeBuilder tb = modb.DefineType(
            "MyClass",
            TypeAttributes.Public | TypeAttributes.Class);

        Type my = tb.CreateType();
        tb.SetParent(typeof(OtherClass<>).MakeGenericType(my));

        my = tb.CreateType();
    }
    catch (Exception e)
    {
        throw;
    }
}

but get this exception:

GenericArguments[0], 'MyClass', on 'MyProject.Factory+OtherClass`1[T]'
violates the constraint of type 'T'.
like image 992
wes Avatar asked Nov 07 '12 19:11

wes


2 Answers

Edit : Here is my final working answer :

        AssemblyName asn = new AssemblyName("test.dll");
        AssemblyBuilder asb = AppDomain.CurrentDomain.DefineDynamicAssembly(
            asn, AssemblyBuilderAccess.RunAndSave, @"D:\test_assemblies");

        ModuleBuilder modb = asb.DefineDynamicModule("test", "test.dll");

        TypeBuilder tb = modb.DefineType(
            "test",
            TypeAttributes.Public | TypeAttributes.Class);
        // Typebuilder is a sub class of Type
        tb.SetParent(typeof(OtherClass<>).MakeGenericType(tb));
        var t2 = tb.CreateType();
        var i = Activator.CreateInstance(t2);

The trick is to call SetParent with a parametrised generic type, the parameter is the typebuilder of the type being constructed itself.


Use the TypeBuilder.SetParent(Type parent) method.

Be careful when using it, exception throwing is deferred to CreateType call :

If parent is null, Object is used as the base type.

In the .NET Framework versions 1.0 and 1.1, no exception is thrown if parent is an interface type, but a TypeLoadException is thrown when the CreateType method is called.

The SetParent method does not check for most invalid parent types. For example, it does not reject a parent type that has no default constructor when the current type has a default constructor, it does not reject sealed types, and it does not reject the Delegate type. In all these cases, exceptions are thrown by the CreateType method.

To build your generic type OtherClass<T>, use the MakeGenericType method :

var genericType = typeof(OtherClass<>).MakeGenericType(typeof(MyClass));
like image 183
theor Avatar answered Sep 18 '22 14:09

theor


You can set the base type afterwards.

TypeBuilder.SetParent(Type)

like image 41
leppie Avatar answered Sep 22 '22 14:09

leppie