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();
}
所以,我的问题:
OpCodes.Call
指令完成的?]下面有一些上下文信息...我有一个客户端,它可以执行服务器公开的方法,只有该方法与此委托公共委托对象[] MyMethod(int code,object [] ...]相匹配。 >
在您的情况下,您只需要动态构建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);