使用名称空间System.Reflection.Emit注入通用方法作为另一个方法的包装[C#]

以下一些上下文信息... 我有一个Client,只有在该方法与此delegate

相匹配的情况下,它才能执行服务器公开的方法
public delegate object[] MyMethod (int code,object[] parameters);

所以...例如,如果我想公开一个执行两个整数之和的新方法,我应该这样做:

private static object[] Sum(int code,object[] parameters)
{
    int firstNumber = int.Parse(parameters[0].ToString()),secondNumber = int.Parse(parameters[1].ToString());

    return new object[1] { firstNumber + secondNumber };
}

避免每种检查(空检查,整数检查等等)都是为了简单起见。 然后,在类的初始化程序中,我应该做类似

的操作
Register(Sum);

Register接受委托MyMethod的情况下,确实只是注册了一个方法(应由该委托执行),并且该方法应具有两个整数类型的参数(.net中为int32)作为输入,而一个参数应作为输入整数(整数32)作为输出结果。

到目前为止,没有什么困难的...但是我想做些额外的事情,以实现更易读的东西...

我的目标是让开发人员写这样的东西:

[ExternalOperationContract]
public static int Sum(int a,int b) => a + b;

其中[ExternalOperationContractAttribute]是临时创建的自定义属性。 我想做的是获取所有带有反射标记有该属性的方法,创建一个具有相同委托的输入和输出参数的DynamicMethod并注入指令,以便新方法的行为与没有委托的方法完全相同。 ExternalOperationCOntractAttribute。

我发现了System.Reflection.Emit命名空间,并且我认为它对该范围很有用。因此,我尝试实现一些方法,但结果却很差,坦率地说,弗兰肯斯坦代码在网络上到处都获取了一些代码。下面是一部分:

public static void Main()
{
    Type[] sumArgs = { typeof(int),typeof(object[]) };

    // Create a dynamic method with the name "Sum",a return type
    // of object[],and two parameters whose types are specified by the
    // array helloArgs. Create the method in the module that
    // defines the Test class.
    DynamicMethod hello = new DynamicMethod("Sum",typeof(object[]),sumArgs,typeof(Test).Module);

    // Get the overload of Console.WriteLine that has one
    // String parameter.
    MethodInfo myMethod =
        typeof(Test).GetMethod("Sum");

    ILGenerator ilgen = hello.GetILGenerator();
    var il = ilgen;    
    il.Emit(OpCodes.Ldarg_0);

    LocalBuilder arr = il.DeclareLocal(typeof(string));
    il.Emit(OpCodes.Ldc_I4_1);
    il.Emit(OpCodes.Newarr,typeof(string));
    il.Emit(OpCodes.Stloc,arr);
    il.Emit(OpCodes.Ldloc,arr);
    il.Emit(OpCodes.Ldc_I4_0);
    il.Emit(OpCodes.Ldarg_1);
    il.Emit(OpCodes.Stelem_I4);

    il.Emit(OpCodes.Ldloc,arr);
    il.Emit(OpCodes.Ldc_I4_0);
    il.Emit(OpCodes.Ldelem_I4);

    il.Emit(OpCodes.Call,myMethod);

    il.Emit(OpCodes.Ret);

    // Create a delegate that represents the dynamic method. This
    // action completes the method,and any further attempts to
    // change the method will cause an exception.
    MethodCallBack hi =
        (MethodCallBack)hello.CreateDelegate(typeof(MethodCallBack));

    Register(hi);

    Console.Read();
} 

所以,我的问题:

  • 如何从输入数组中获取元素,将其转换为适当的类型,然后将其发送给DynamicMethod(我认为这部分是通过OpCodes.Call指令完成的)?
  • 如何使用Emit名称空间将多个对象返回到对象数组中?
  • 无论如何,解释比直接解决方案更可取:)甚至可以理解一种编程指令。我希望有人提出一些建设性的批评意见,例如“为什么让事情变得简单?” ...是的,我知道,我也很想知道:)也许看到并学习一些新知识,并尝试使代码更具可读性和苗条性,对于开发人员来说根本不是一件坏事。

非常感谢您。

yzp0754 回答:使用名称空间System.Reflection.Emit注入通用方法作为另一个方法的包装[C#]

我的理解是,只要您不修改已编译的程序集,就无需降低到IL级别并使用Emit。

在您的情况下,您只需要动态构建MyMethod的实例,即可使用对象数组中提供的参数调用Sum。因此,您所需要做的就是这个:

MyMethod m = (code,p) => new object[1] { Sum((int)p[0],(int)p[1]) };
Register(m);

这就是Express Trees有用的时候。假设您使用反射功能为MethodInfo找到了Sum,则可以像上面那样构建上述功能:

private static MyMethod BuildMyMethod(MethodInfo mi)
{
    var code = Expression.Parameter(typeof(int),"code");
    var objParameters = Expression.Parameter(typeof(object[]),"p");

    // (pi.ParameterType)p[0]
    // e.g. (int)p[0]
    var parameters = mi.GetParameters().Select(
        (pi,index) => Expression.Convert(Expression.ArrayIndex(
            objParameters,Expression.Constant(index)
        ),pi.ParameterType));

    // e.g Sum(((int)p[0]),(int)p[1])
    var callMethod = Expression.Call(mi,parameters);

    // new object[] { Sum(((int)p[0]),(int)p[1]) }
    var returnResult = Expression.NewArrayInit(
        typeof(object),Expression.Convert(callMethod,typeof(object)));

    var myMethod = Expression.Lambda<MyMethod>(returnResult,code,objParameters).Compile();
    return myMethod;
}

然后您可以像这样注册Sum

var myMethod = BuildMyMethod(sumMethod);

// If you call the built delegate:
// result = new object[1] { 3 }
var result = myMethod(1,new object[] { 1,2 });

Register(myMethod);
本文链接:https://www.f2er.com/2580073.html

大家都在问