2

I am trying to use .NET reflection to emit a class and some interfaces which the class implements. However, I cannot seem to correctly implement the interfaces that are generic, as that results in errors when the class type is created.

The code below is supposed to emit classes and interfaces equivalent to the following C#

public interface MyInterface1
{
    void IfaceMethod(int arg);
}

public interface MyInterface2<T>
{
    void IfaceMethod(int arg);
}

public interface MyInterface3<T>
{
    void IfaceMethod(T arg);
}

public class MyClass : MyInterface1, MyInterface2<double>, MyInterface3<int>
{
    void MyInterface1.IfaceMethod(int arg)
    {
        Console.WriteLine(...);
    }

    void MyInterface2<double>.IfaceMethod(int arg)
    {
        Console.WriteLine(...);
    }

    void MyInterface3<int>.IfaceMethod(int arg)
    {
        Console.WriteLine(...);
    }
}

My code using reflection to emit this is

using System.Reflection;
using System.Reflection.Emit;

class Test
{
    static void Main()
    {
        string example_name = "Interface example";
        AssemblyName asm_name = new AssemblyName(example_name);
        AssemblyBuilder asm_builder = AssemblyBuilder.DefineDynamicAssembly(asm_name, AssemblyBuilderAccess.Run);
        ModuleBuilder module_builder = asm_builder.DefineDynamicModule(example_name);

        TypeBuilder class_builder = module_builder.DefineType("MyClass", TypeAttributes.Public | TypeAttributes.Class);

        TypeBuilder interface1_builder = module_builder.DefineType("MyInterface1", TypeAttributes.Public | TypeAttributes.Interface | TypeAttributes.Abstract);
        TypeBuilder interface2_builder = module_builder.DefineType("MyInterface2", TypeAttributes.Public | TypeAttributes.Interface | TypeAttributes.Abstract);
        TypeBuilder interface3_builder = module_builder.DefineType("MyInterface3", TypeAttributes.Public | TypeAttributes.Interface | TypeAttributes.Abstract);
        Type[] interface2_generics = interface2_builder.DefineGenericParameters(["T"]);
        Type[] interface3_generics = interface3_builder.DefineGenericParameters(["T"]);

        MethodAttributes method_attributes = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Abstract;
        interface1_builder.DefineMethod("IfaceMethod", method_attributes, null, [typeof(int)]);
        interface2_builder.DefineMethod("IfaceMethod", method_attributes, null, [typeof(int)]);
        interface3_builder.DefineMethod("IfaceMethod", method_attributes, null, interface3_generics);

        Type interface1_type = interface1_builder.CreateType();
        Type interface2_type = interface2_builder.CreateType();
        Type interface3_type = interface3_builder.CreateType();

        /* Now let MyClass implement IfaceMethod from MyInterface1, MyInterface2<double> and MyInterface3<int> */
        List<Type> interfaces = new List<Type>();
        // Interface1 always works
        interfaces.Add(interface1_type);

        // Interface2 errors with: Unhandled exception. System.TypeLoadException: Type 'MyClass' from assembly
        //   'Interface example, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' tried to override
        //   method 'fn_0' but does not implement or inherit that method. 
        interfaces.Add(interface2_type.MakeGenericType([typeof(double)]));

        // Interface3 errors with: Unhandled exception. System.TypeLoadException: Signature of the body and
        //   declaration in a method implementation do not match.  Type: 'MyClass'.  Assembly:
        //   'Interface example, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
        interfaces.Add(interface3_type.MakeGenericType([typeof(int)]));

        List<MethodInfo> methods = new List<MethodInfo>();
        for(int i = 0; i < interfaces.Count; i++){
            Type interface_type = interfaces[i];
            class_builder.AddInterfaceImplementation(interface_type);

            string method_name = "fn_"+i;
            MethodBuilder class_method = class_builder.DefineMethod(method_name, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final, null, [typeof(int)]);
            ILGenerator il = class_method.GetILGenerator();
            il.EmitWriteLine("Hello from " + method_name + " implementing IfaceMethod from " + interface_type.Name);
            il.Emit(OpCodes.Ret);

            MethodInfo interface_method = interface_type.GetMethod("IfaceMethod");
            class_builder.DefineMethodOverride(class_method, interface_method);

            methods.Add(interface_method);
        }

        Type class_type = class_builder.CreateType();
        object obj = Activator.CreateInstance(class_type);
        Object[] args = [123];
        foreach(MethodInfo method in methods){
            method.Invoke(obj, args);
        }
    }
}

I get error messages from the class_builder.CreateType() call, unless I comment out the interfaces.Add(...) lines that adds my generic interfaces (interface2 and interface3) to the list. The error message when I attempt to implement MyInterface2 is

Unhandled exception. System.TypeLoadException: Type 'MyClass' from 
assembly 'Interface example, Version=0.0.0.0, Culture=neutral, 
PublicKeyToken=null' tried to override method 'fn_0' but does not 
implement or inherit that method.
   at System.Reflection.Emit.RuntimeTypeBuilder.CreateTypeNoLock()
   at System.Reflection.Emit.RuntimeTypeBuilder.CreateTypeInfoImpl()
   at Test.Main() in C:\tmp\interface-issue\Program.cs:line 62

and when I attempt to implement MyInterface3 the error is

Unhandled exception. System.TypeLoadException: Signature of the body 
and declaration in a method implementation do not match.
  Type: 'MyClass'.  Assembly: 'Interface example, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
   at System.Reflection.Emit.RuntimeTypeBuilder.CreateTypeNoLock()
   at System.Reflection.Emit.RuntimeTypeBuilder.CreateTypeInfoImpl()
   at Test.Main() in C:\tmp\interface-issue\Program.cs:line 62

I would expect all the interface implementations to work, since the interface method signatures are all equivalent given the concrete type arguments applied to my generic interfaces.

Any hints at what I am missing here would be appreciated. I have tried this using both .NET 8.0 and .NET 9.0, and there is no difference.

Edit: fixed version based on the accepted solution below.

using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Collections.Generic;
using System.Linq;

class Test
{
    static void Main()
    {
        string example_name = "Interface example";
        AssemblyName asm_name = new AssemblyName(example_name);
        AssemblyBuilder asm_builder = AssemblyBuilder.DefineDynamicAssembly(asm_name, AssemblyBuilderAccess.Run);
        ModuleBuilder module_builder = asm_builder.DefineDynamicModule(example_name);

        TypeBuilder class_builder = module_builder.DefineType("MyClass", TypeAttributes.Public | TypeAttributes.Class);

        TypeBuilder interface1_builder = module_builder.DefineType("MyInterface1", TypeAttributes.Public | TypeAttributes.Interface | TypeAttributes.Abstract);
        TypeBuilder interface2_builder = module_builder.DefineType("MyInterface2", TypeAttributes.Public | TypeAttributes.Interface | TypeAttributes.Abstract);
        TypeBuilder interface3_builder = module_builder.DefineType("MyInterface3", TypeAttributes.Public | TypeAttributes.Interface | TypeAttributes.Abstract);
        Type[] interface2_generics = interface2_builder.DefineGenericParameters(["T"]);
        Type[] interface3_generics = interface3_builder.DefineGenericParameters(["T"]);

        MethodAttributes method_attributes = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Abstract;
        List<MethodBuilder> interface_method_builders = new List<MethodBuilder>();
        interface_method_builders.Add(interface1_builder.DefineMethod("IfaceMethod", method_attributes, null, [typeof(int)]));
        interface_method_builders.Add(interface2_builder.DefineMethod("IfaceMethod", method_attributes, null, [typeof(int)]));
        interface_method_builders.Add(interface3_builder.DefineMethod("IfaceMethod", method_attributes, null, interface3_generics));
        
        interface1_builder.CreateType();
        interface2_builder.CreateType();
        interface3_builder.CreateType();
        
        List<Type> interfaces = new List<Type>();
        interfaces.Add(interface1_builder);
        interfaces.Add(interface2_builder.MakeGenericType([typeof(double)]));
        interfaces.Add(interface3_builder.MakeGenericType([typeof(int)]));
        
        for(int i = 0; i < interfaces.Count; i++){
            Type interface_type = interfaces[i];
            MethodInfo interface_method = interface_method_builders[i];
            try
            {
                interface_method = TypeBuilder.GetMethod(interface_type, interface_method); /* in case it was from a generic interface*/
            }
            catch(Exception)
            {
            }
            
            class_builder.AddInterfaceImplementation(interface_type);

            string method_name = "fn_"+i;
            MethodBuilder class_method = class_builder.DefineMethod(method_name, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final, null, [typeof(int)]);
            ILGenerator il = class_method.GetILGenerator();
            il.EmitWriteLine("Hello from " + method_name + " implementing IfaceMethod from " + interface_type.Name);
            il.Emit(OpCodes.Ret);

            class_builder.DefineMethodOverride(class_method, interface_method);
        }

        Type class_type = class_builder.CreateType();
        object obj = Activator.CreateInstance(class_type);
        List<MethodInfo> methods = class_type.GetInterfaces().Select(i => i.GetMethod("IfaceMethod")).ToList();
        Object[] args = [123];
        foreach(MethodInfo method in methods){
            method.Invoke(obj, args);
        }
    }
}
0

1 Answer 1

1

If we focus just on the second interface (same principle applies to third interface), I think the main point is to call MakeGenericType on the TypeBuilder interface instance and not the created interface Type instance. Check the Remarks section in the docs on TypeBuilder.MakeGenericType in particular:

Use this method when your emitted code requires a type constructed from the current generic type definition.

Some demo code:

string example_name = "Interface example";
AssemblyName asm_name = new AssemblyName(example_name);
AssemblyBuilder asm_builder = AssemblyBuilder.DefineDynamicAssembly(asm_name, AssemblyBuilderAccess.Run);
ModuleBuilder module_builder = asm_builder.DefineDynamicModule(example_name);

TypeBuilder interface2_open_builder = module_builder.DefineType("MyInterface2", TypeAttributes.Public | TypeAttributes.Interface | TypeAttributes.Abstract);
interface2_open_builder.DefineGenericParameters(["T"]);
MethodAttributes method_attributes = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Abstract;
MethodBuilder interface2_method_builder = interface2_open_builder.DefineMethod("IfaceMethod", method_attributes, null, [typeof(int)]);
Type interface_open_created_type = interface2_open_builder.CreateType();

// we need to call MakeGenericType on the Builder, and not the created type

Type interface2_closed_type = interface2_open_builder.MakeGenericType([typeof(double)]);
MethodInfo interface_method = TypeBuilder.GetMethod(interface2_closed_type, interface2_method_builder);
//Type interface2_closed_type = interface_open_created_type.MakeGenericType([typeof(double)]);
//MethodInfo interface_method = interface2_closed_type.GetMethod("IfaceMethod");

TypeBuilder class_builder = module_builder.DefineType("MyClass", TypeAttributes.Public | TypeAttributes.Class);
class_builder.AddInterfaceImplementation(interface2_closed_type);

// IL Generation
string method_name = "fn_0";
MethodBuilder class_method = class_builder.DefineMethod(method_name, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final, null, [typeof(int)]);
ILGenerator il = class_method.GetILGenerator();
il.EmitWriteLine("Hello from " + method_name + " implementing IfaceMethod from " + interface2_closed_type.Name);
il.Emit(OpCodes.Ret);
class_builder.DefineMethodOverride(class_method, interface_method);

Type class_type = class_builder.CreateType();

// some testing code
MethodInfo methodInfo = class_type.GetInterfaces()
    .Where(x => x.Name == "MyInterface2")
    .FirstOrDefault()
    .GetMethod("IfaceMethod");

object obj = Activator.CreateInstance(class_type);
methodInfo.Invoke(obj, [42]);

If you toggle the two commented lines, you'd get your initial state from the question - getting the same error as before.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks a lot! This solved my problem. I will update my original question to include a fixed version of the code, based on what you said here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.