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

问题描述 投票:0回答:1
下面有一些上下文信息...我有一个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)作为输入,而一个整数类型的参数( int 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名称空间将多个对象返回到对象数组中?
  • 无论如何,最好是一种解释而不是直接的解决方案:)甚至可以采用一种编程指令的方式。我希望有人提出一些建设性的批评意见,例如“为什么使简单的事情变得复杂?”。 ...是的,我知道,我也很想知道:)也许看到并学习一些新知识,并尝试使代码更具可读性和苗条性,对于开发人员来说根本不是一件坏事。
  • 非常感谢您。

下面有一些上下文信息...我有一个客户端,它可以执行服务器公开的方法,只有该方法与此委托公共委托对象[] MyMethod(int code,object [] ...]相匹配。 >

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

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

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

那时Express Trees变得有用。假设您已使用反射找到了MethodInfoSum,则可以像上面那样构建上述函数:

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);

c# system.reflection reflection.emit
1个回答
0
投票
我的理解是,只要您不修改已编译的程序集,就无需降低到IL级别并使用Emit。
© www.soinside.com 2019 - 2024. All rights reserved.