Ваш код будет работать как есть, если вызываемый метод будет статическим:
public static long RealSquare(int val) => val * val;
public void test()
{
Func<int, long> realSquareFunc = RealSquare;
// ...
Однако на самом деле лямбда realSquareFunc = (val) => val * val
компилируется как метод экземпляра скрытого класса. Чтобы вызвать метод экземпляра, экземпляр должен быть сначала помещен в стек до аргументов метода. Вызовы методов экземпляра также обычно используют код операции Callvirt
(независимо от того, являются ли они виртуальными, потому что этот код операции выполняет проверку нулевых ссылок):
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)
}
}
Вызов целевого метода лямбды напрямую более сложен из-за задействованного класса, сгенерированного компилятором, но если вы хотите вызвать делегат в целом, это работает следующим образом:
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)
}
}
person
kalimag
schedule
17.05.2020