Wie verwende ich .NET Reflection, um generische Schnittstellen und eine Klasse auszugeben, die sie implementiert?C#

Ein Treffpunkt für C#-Programmierer
Anonymous
 Wie verwende ich .NET Reflection, um generische Schnittstellen und eine Klasse auszugeben, die sie implementiert?

Post by Anonymous »

Ich versuche, .NET Reflection zu verwenden, um eine Klasse und einige Schnittstellen auszugeben, die die Klasse implementiert. Es scheint mir jedoch nicht möglich zu sein, die generischen Schnittstellen korrekt zu implementieren, da dies beim Erstellen des Klassentyps zu Fehlern führt.
Der folgende Code soll Klassen und Schnittstellen ausgeben, die dem folgenden C# entsprechen

Code: Select all

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

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

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

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

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

void MyInterface3.IfaceMethod(int arg)
{
Console.WriteLine(...);
}
}
Mein Code, der Reflektion verwendet, um dies auszugeben, ist

Code: Select all

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 and MyInterface3 */
List interfaces = new List();
// 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 methods = new List();
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);
}
}
}
Ich erhalte Fehlermeldungen vom class_builder.CreateType()-Aufruf, es sei denn, ich kommentiere die Zeilen interfaces.Add(...) aus, die meine generischen Schnittstellen (interface2 und interface3) zur Liste hinzufügen.
Die Fehlermeldung, wenn ich versuche, MyInterface2 zu implementieren, lautet

Code: Select all

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
und wenn ich versuche, MyInterface3 zu implementieren, ist der Fehler

Code: Select all

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
Ich gehe davon aus, dass alle Schnittstellenimplementierungen funktionieren, da die Signaturen der Schnittstellenmethoden angesichts der konkreten Typargumente, die auf meine generischen Schnittstellen angewendet werden, alle gleichwertig sind.
Jeder Hinweis darauf, was mir hier fehlt, wäre dankbar. Ich habe dies sowohl mit .NET 8.0 als auch mit .NET 9.0 versucht und es gibt keinen Unterschied.
Bearbeiten: korrigierte Version basierend auf der unten akzeptierten Lösung.

Code: Select all

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 interface_method_builders = new List();
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 interfaces = new List();
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 methods = class_type.GetInterfaces().Select(i => i.GetMethod("IfaceMethod")).ToList();
Object[] args = [123];
foreach(MethodInfo method in methods){
method.Invoke(obj, args);
}
}
}

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post