IL生成代理以用户定义Func

问题描述 投票:1回答:1

我正在尝试动态生成程序集以调用用户创建的函数。

private MethodBuilder DefineGetMethod<TInput, TReturn>(
     TypeBuilder tb, MethodDescriptor methodInfo, Func<TInput, TReturn> dynamicMethod)
{
    //Define the function
    var dynamicMethodBuilder = tb.DefineMethod(methodInfo.MethodName,
              MethodAttributes.Public,
              methodInfo.ReturnType, methodInfo.InputParameters.Select(x => x.Type).ToArray());

    //Define the labels for the method inputs
    for(var i = 0; i < methodInfo.InputParameters.Length; i++ )
    {
        // Position 0 is the return value, 1 is the 1st param, 2 is 2nd, etc.
        var position = 1 + i;
        var inputParam = methodInfo.InputParameters[i];
        dynamicMethodBuilder.DefineParameter(position, ParameterAttributes.None, inputParam.Name);
    }

    var ilGenerator = dynamicMethodBuilder.GetILGenerator();

    //Loads arg1
    ilGenerator.Emit(OpCodes.Ldarg_1);

    //Not sure how to pass the arg1 to the method body to return
    var ilMethodBody = dynamicMethod.Method.GetMethodBody().GetILAsByteArray();

    //Generates return
    ilGenerator.Emit(OpCodes.Ret);

}

编辑我反编译了现有代码,以与反编译的现有代码类似的方式调用该方法,但我仍然无法使它正常工作

// Argument 1 of dynamic method is argument array.
myMethodIL.Emit(OpCodes.Ldarg_1);
myMethodIL.Emit(OpCodes.Callvirt, method.Method);
myMethodIL.Emit(OpCodes.Stloc_0);
myMethodIL.Emit(OpCodes.Ldloc_0);
myMethodIL.Emit(OpCodes.Ret);

如何将加载的参数传递给ilMethodBody并返回?

c# reflection .net-core code-generation
1个回答
0
投票

首先,必须确保TReturn等于methodInfo.ReturnType,并且TInput等于methodInfo.InputParameters的第一个。

如果dynamicMethod是静态方法委托,它将很容易像:

private MethodBuilder DefineGetMethod<TInput, TReturn>(TypeBuilder tb, MethodDescriptor methodInfo, Func<TInput, TReturn> dynamicMethod)
{
    var dynamicMethodBuilder = tb.DefineMethod(methodInfo.MethodName,
                MethodAttributes.Public | MethodAttributes.HideBySig,
                methodInfo.ReturnType, methodInfo.InputParameters.Select(x => x.Type).ToArray());

    var ilGenerator = dynamicMethodBuilder.GetILGenerator();

    ilGenerator.Emit(OpCodes.Ldarg_1);
    ilGenerator.Emit(OpCodes.Call, dynamicMethod.Method);
    ilGenerator.Emit(OpCodes.Ret);

    return dynamicMethodBuilder;
}

但是如果dynamicMethod可能是一个实例方法(带有实例方法的lambda),则几乎不会。调用实例方法需要先将实例压入堆栈,但是Emit仅允许压入常量值,例如int,string。

我只能想到一种方法,声明一个字段来存储dynamicMethod,通过反射在构建类型之后设置字段值:

private Type DefineGetMethod<TInput, TReturn>(TypeBuilder tb, MethodDescriptor methodInfo, Func<TInput, TReturn> dynamicMethod)
{
    var fieldBuilder = tb.DefineField("_func", dynamicMethod.GetType(), FieldAttributes.Private | FieldAttributes.Static);

    var dynamicMethodBuilder = tb.DefineMethod(methodInfo.MethodName,
                MethodAttributes.Public | MethodAttributes.HideBySig,
                methodInfo.ReturnType, methodInfo.InputParameters.Select(x => x.Type).ToArray());

    var ilGenerator = dynamicMethodBuilder.GetILGenerator();

    // load static field _func onto stack
    ilGenerator.Emit(OpCodes.Ldsfld, fieldBuilder);
    // load arg1 onto stack
    ilGenerator.Emit(OpCodes.Ldarg_1);
    // call _func.Invoke(..)
    ilGenerator.Emit(OpCodes.Callvirt, dynamicMethod.GetType().GetMethod("Invoke"));
    ilGenerator.Emit(OpCodes.Ret);

    var type = tb.CreateType();
    var field = type.GetField("_func", BindingFlags.NonPublic | BindingFlags.Static);
    // store dynamicMethod into static field _func
    field.SetValue(null, dynamicMethod);
    return type;
}
© www.soinside.com 2019 - 2024. All rights reserved.