0

I am trying to dynamically build a type with a method that calls into an external delegate by using System.Reflection.Emit. However when I try to call this method, my program crashes with the exception in the title at the method call. Here's my code so far:

private static void TestMethodReal() => Console.Out.WriteLine("Inside TestMethod");

// In Main()
var method = typeof(Program).GetMethod(nameof(TestMethodReal), BindingFlags.Static | BindingFlags.NonPublic)!;

var builder = MyTypeBuilder.GetTypeBuilder("TestType");
var testMethod = builder.DefineMethod("TestMethod", MethodAttributes.Public, typeof(void), Type.EmptyTypes);

var generator = testMethod.GetILGenerator();
generator.EmitCall(OpCodes.Callvirt, method, null);
generator.Emit(OpCodes.Ret);

dynamic inst = Activator.CreateInstance(builder.CreateType()!)!;
inst.TestMethod(); // <--- Exception is thrown here

The MyTypeBuilder class and GetTypeBuilder method is from this answer, slightly modified to accept a parameter for the type's name.

This program is supposed to create a new dynamic class with a method called TestMethod that calls the actual TestMethodReal method, instantiate the class, and call the method.

What am I missing?

7
  • This is strange BindingFlags.Static | BindingFlags.NonPublic)!;. What is that ! for?. And again in the line just before your error Activator.CreateInstance(builder.CreateType()!)!; Commented Dec 29, 2021 at 16:04
  • It's a somewhat annoying feature of one of the new C# versions (9.0 or 10.0 I believe). GetMethod returns a nullable MethodInfo? type (note the '?' at the end). The ! basically ensures that the return value is not null, otherwise my IDE will complain about it and draw an ugly yellow squiggly line underneath it. Same with CreateType and CreateInstance, returning nullable Type? and object? types respectively. Commented Dec 29, 2021 at 16:10
  • 1
    Call the target method directly instead of requesting virtual dispatch: generator.EmitCall(OpCodes.Call, method, Types.EmptyTypes) - you don't need the runtime to resolve TestMethodReal for you since you already have an exact reference to it via method :) Commented Dec 29, 2021 at 16:13
  • Are you sure you are running on .NET 6 / .NET Core cause AppDomain.DefineDynamicAssembly used in linked answer is .NET Framework only. Commented Dec 29, 2021 at 16:16
  • @GuruStron I forgot to mention this, but the .NET Core replacement of that method is AssemblyBuilder.DefineDynamicAssembly, which I also replaced in the linked GetTypeBuilder method. Commented Dec 29, 2021 at 16:20

1 Answer 1

2

You're using the wrong dispatch mechanism!

OpCodes.Callvirt is for virtual method calls, eg. overridable instance methods, the resolution of which needs to be deferred until runtime.

For static method invocation you'll want a plain old OpCodes.Call instruction instead:

generator.EmitCall(OpCodes.Call, method, Types.EmptyTypes);
Sign up to request clarification or add additional context in comments.

Comments

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.