I want to call a function with a parameter with the reflection.emit
API. In the following is what I have at the moment. But when I run it, it throws the following exception: System.InvalidProgramException : Common Language Runtime detected an invalid program
. So my question is what I have wrong in my code snippet below? Can someone help me out?
public class Test
{
public void test()
{
Func<int, long> realSquareFunc = (val) => val * val;
Type[] methodArgs = { typeof(int) };
DynamicMethod squareIt = new DynamicMethod(
"SquareIt",
typeof(long),
methodArgs,
typeof(Test).Module)
;
ILGenerator il = squareIt.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // Save parameter on stack
il.Emit(OpCodes.Call, realSquareFunc.Method); // Call function with input as parameter
il.Emit(OpCodes.Ret); // Return value from function call before
var myMethod = (Func<int, long>)squareIt.CreateDelegate(realSquareFunc.GetType());
var result = myMethod.Invoke(4); // Should be 16 (4*4)
}
}
Your code would work as is if the called method was a static method:
public static long RealSquare(int val) => val * val;
public void test()
{
Func<int, long> realSquareFunc = RealSquare;
// ...
However, the realSquareFunc = (val) => val * val
lambda is actually compiled as an instance method of a hidden class. To call an instance method, the instance must be pushed onto the stack first, before the method arguments. Instance method calls also typically use the Callvirt
opcode (regardless of whether they're virtual, because this opcode does null reference checking):
public class Test
{
public long RealSquare(int val) => val * val;
public void test()
{
Func<int, long> realSquareFunc = RealSquare;
// pass the instance we want to call the method on in as well
Type[] methodArgs = { typeof(Test), typeof(int) };
DynamicMethod squareIt = new DynamicMethod(
"SquareIt",
typeof(long),
methodArgs,
typeof(Test).Module)
;
ILGenerator il = squareIt.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // Push the target instance onto stack
il.Emit(OpCodes.Ldarg_1); // Save parameter on stack
il.Emit(OpCodes.Callvirt, realSquareFunc.Method); // Call function with input as parameter
il.Emit(OpCodes.Ret); // Return value from function call before
var myMethod = (Func<Test, int, long>)squareIt.CreateDelegate(typeof(Func<Test, int, long>));
var result = myMethod.Invoke(this, 4); // Should be 16 (4*4)
}
}
Calling the target method of a lambda directly is more complicated due to the compiler-generated class involved, but if you want to call a delegate in general it works like this:
public class Test
{
public void test()
{
Func<int, long> realSquareFunc = (val) => val * val;
// pass the delegate we want to call into the method
Type[] methodArgs = { realSquareFunc.GetType(), typeof(int) };
DynamicMethod squareIt = new DynamicMethod(
"SquareIt",
typeof(long),
methodArgs,
typeof(Test).Module)
;
ILGenerator il = squareIt.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // Push the delegate onto stack
il.Emit(OpCodes.Ldarg_1); // Save parameter on stack
il.Emit(OpCodes.Callvirt, realSquareFunc.GetType().GetMethod("Invoke")); // Invoke delegate
il.Emit(OpCodes.Ret); // Return value from function call before
var myMethod = (Func<Func<int, long>, int, long>)squareIt
.CreateDelegate(typeof(Func<Func<int, long>, int, long>));
var result = myMethod.Invoke(realSquareFunc, 4); // Should be 16 (4*4)
}
}