源代码:https://www.pastefile.com/4mzhyg
我正在尝试创建格式的委托:
public delegate TReturn MethodCallerR<TTarget, TReturn>(ref TTarget target, object[] args);
/// <summary>
/// Generates a strongly-typed open-instance delegate to invoke the specified method
/// </summary>
public static MethodCallerR<TTarget, TReturn> DelegateForCallR<TTarget, TReturn>(this MethodInfo method) {
int key = GetKey<TTarget, TReturn>(method, kMethodCallerName);
Delegate result;
if (cache.TryGetValue(key, out result))
return (MethodCallerR<TTarget, TReturn>)result;
return GenDelegateForMember<MethodCallerR<TTarget, TReturn>, MethodInfo>(
method, key, kMethodCallerName, GenMethodInvocationR<TTarget>,
typeof(TReturn), typeof(TTarget).MakeByRefType(), typeof(object[]));
}
弱类型函数:
public static MethodCallerR<object, object> DelegateForCallR(this MethodInfo method) {
return DelegateForCallR<object, object>(method);
}
委托创建者:
static TDelegate GenDelegateForMember<TDelegate, TMember>(TMember member, int key, string dynMethodName,
Action<TMember> generator, Type returnType, params Type[] paramTypes)
where TMember : MemberInfo
where TDelegate : class {
var dynMethod = new DynamicMethod(dynMethodName, returnType, paramTypes, true);
emit.il = dynMethod.GetILGenerator();
generator(member);
var result = dynMethod.CreateDelegate(typeof(TDelegate));
cache[key] = result;
return (TDelegate)(object)result;
}
和IL代码生成器:
static void GenMethodInvocationR<TTarget>(MethodInfo method) {
var weaklyTyped = typeof(TTarget) == typeof(object);
// push arguments in order to call method
var prams = method.GetParameters();
var imax = prams.Length;
for (int i = 0; i < imax; i++) {
emit.ldarg1() // stack<= paramsValuesArray[] //push array
.ldc_i4(i) // stack<= index push(index)
.ldelem_ref(); // stack[top]<=paramsValuesArray[i]
var param = prams[i];
var dataType = param.ParameterType;
if (dataType.IsByRef)
dataType = dataType.GetElementType();
emit.unbox_any(dataType);
emit.declocal(dataType);
emit.stloc(i);
}
if (!method.IsStatic)
{
var targetType = weaklyTyped ? method.DeclaringType : typeof(TTarget);
emit.ldarg0(); //stack[top]=target;
emit.ldind_ref();//stack[top]=ref target;
if (weaklyTyped)
emit.unbox_any(targetType); //stack[top]=(TargetType)target;
}
//load parms from local 'list' to evaluation 'steak'
for (int i = 0; i < imax; i++) {
var param = prams[i];
emit.ifbyref_ldloca_else_ldloc(i, param.ParameterType);
}
// perform the correct call (pushes the result)
emit.callorvirt(method);
//check of ref and out params and
for (int i = 0; i < prams.Length; i++) {
var paramType = prams[i].ParameterType;
if (paramType.IsByRef)
{
var byRefType = paramType.GetElementType();
emit.ldarg1() // stack<= paramsValuesArray[]
.ldc_i4(i) // stack<= i //push(index)
.ldloc(i); // stack<= list[i] //push local list element at 'i' on steak
if (byRefType.IsValueType)
emit.box(byRefType); // if ex. stack[top] =(object)stack[top]
emit.stelem_ref(); // // paramsValuesArray[i]= pop(stack[top]);
}
}
if (method.ReturnType == typeof(void))
emit.ldnull();
else if (weaklyTyped)
emit.ifvaluetype_box(method.ReturnType);
emit.ret();
}
我使用的结构示例是带有Set方法的Vector3:
public struct Vector3{
public float x;
public float y;
public float z;
public Vector3(float x,float y,float z){
this.x=x;
this.y=y;
this.z=z;
}
public void Set(float x,float y,float z){
this.x=x;
this.y=y;
this.z=z;
}
}
所以我在做
object vector3Obj=new Vector3(4,5,6);
MethodInfo method=typeof(Vector3).GetMethod("Set");
MethodCallerR<object,object> m = methodInfo.DelegateForCallR();
m(ref vector3Obj,new object[]{1f,2f,3f});
Console.Write(vector3.x);
永远不会到达执行委托,但是在调用Delegate.CreateDelegate时会炸毁:
查看行: dynMethod.CreateDelegate(typeof(TDelegate));
有错误:
InvalidProgramException :(包装器动态方法)对象中的无效IL代码:MC <>(对象&,对象[]):IL_004f:调用0x00000009 引用实际的IL代码时在emmit.call(方法)处出错,但是当我使用辅助函数时:
FastReflection.GenDebugAssembly<object>("my.dll",null,null,methodInfo,vector3Obj.GetType(),new Type[]{typeof(float),typeof(float),typeof(float)});
这将生成my.dll并使用ILSpy打开,我可以看到从相同的IL代码生成的方法也很好。
public static object MethodCallerR(ref object ptr, object[] array)
{
float num = (float)array[0];
float num2 = (float)array[1];
float num3 = (float)array[2];
((Vector3)ptr).Set(num, num2, num3);
return null;
}
答案 0 :(得分:0)
我找到了解决方案。首先,尽管生成的方法是正确的,但没有设置引用通过。所以我试图生成另一种方法。
public static object MethodCallerR(ref object ptr, object[] array)
{
float num = (float)array[0];
float num2 = (float)array[1];
float num3 = (float)array[2];
Vector3 vector = (Vector3)ptr;
vector.Set(num, num2, num3);
ptr = vector;
return null;
}
所以我将IL代码更改为:
static void GenMethodInvocationR<TTarget>(MethodInfo method) {
//this version winxalex generates more optimized code
//Generated
// public static object MethodCallerR(ref object ptr, object[] array)
// {
// float num = (float)array[0];
// float num2 = (float)array[1];
// float num3 = (float)array[2];
// Vector3 vector = (Vector3)ptr;
// vector.Set(num, num2, num3);
// ptr = vector;
// return null;
// }
//
var weaklyTyped = typeof(TTarget) == typeof(object);
var targetType = weaklyTyped ? method.DeclaringType : typeof(TTarget);
var isNotStatic = !method.IsStatic;
LocalBuilder targetLocal = null;
// push arguments in order to call method
var prams = method.GetParameters();
var imax = prams.Length;
for (int i = 0; i < imax; i++) {
emit.ldarg1() // stack<= paramsValuesArray[] //push array
.ldc_i4(i) // stack<= index push(index)
.ldelem_ref(); // stack[top]<=paramsValuesArray[i]
var param = prams[i];
var dataType = param.ParameterType;
if (dataType.IsByRef)
dataType = dataType.GetElementType();
emit.unbox_any(dataType);
emit.declocal(dataType);
emit.stloc(i);
}
if (isNotStatic)
{
emit.ldarg0(); //stack[top]=target;
emit.ldind_ref();//stack[top]=ref target;
if (weaklyTyped)
emit.unbox_any(targetType); //stack[top]=(TargetType)target;
targetLocal = emit.declocal(targetType); //TargetType tmpTarget; list[0]=tmpTarget;
emit.stloc(targetLocal) //list[0]=stack.pop();
.ifclass_ldloc_else_ldloca(targetLocal, targetType);
}
//load parms from local 'list' to evaluation 'steak'
for (int i = 0; i < imax; i++) {
var param = prams[i];
emit.ifbyref_ldloca_else_ldloc(i, param.ParameterType);
}
// perform the correct call (pushes the result)
emit.callorvirt(method);
if (isNotStatic && targetType.IsValueType) {
emit.ldarg0().ldloc(targetLocal).box(targetType).stind_ref();
}
//check of ref and out params and
for (int i = 0; i < prams.Length; i++) {
var paramType = prams[i].ParameterType;
if (paramType.IsByRef)
{
var byRefType = paramType.GetElementType();
emit.ldarg1() // stack<= paramsValuesArray[]
.ldc_i4(i) // stack<= i //push(index)
.ldloc(i); // stack<= list[i] //push local list element at 'i' on steak
if (byRefType.IsValueType)
emit.box(byRefType); // if ex. stack[top] =(object)stack[top]
emit.stelem_ref(); // // paramsValuesArray[i]= pop(stack[top]);
}
}
if (method.ReturnType == typeof(void))
emit.ldnull();
else if (weaklyTyped)
emit.ifvaluetype_box(method.ReturnType);
emit.ret();
//vexe orignial modified by winxalex to use reference of object (ref obj) and to return value to reference
//GENERATES
// public static object MethodCallerR(ref object ptr, object[] array)
// {
// Vector3 vector = (Vector3)ptr;
// float num = (float)array[0];
// float arg_56_1 = num;
// float num2 = (float)array[1];
// float arg_56_2 = num2;
// float num3 = (float)array[2];
// vector.Set(arg_56_1, arg_56_2, num3);
// ptr = vector;
// return null;
// }
// var weaklyTyped = typeof(TTarget) == typeof(object);
// var targetType = weaklyTyped ? method.DeclaringType : typeof(TTarget);
// // push target if not static (instance-method. in that case first arg is always 'this')
// if (!method.IsStatic)
// {
//
// emit.declocal(targetType); //TargetType tmpTarget; list[0]=tmpTarget;
// emit.ldarg0(); //stack[0]=target;
// emit.ldind_ref();//stack[top]=ref target;
// if (weaklyTyped)
// emit.unbox_any(targetType); //stack[0]=(TargetType)target;
// emit.stloc0() //list[0]=stack.pop();
// .ifclass_ldloc_else_ldloca(0, targetType);
// // if (type.IsValueType) stack[0]=list[0].address, else stack[0]=list[0];
// // if (type.IsValueType) emit.ldloca(idx); else emit.ldloc(idx); return this;
// }
//
// // if method wasn't static that means we declared a temp local to load the target
// // that means our local variables index for the arguments start from 1
// int localVarStart = method.IsStatic ? 0 : 1;
//
// // push arguments in order to call method
// var prams = method.GetParameters();
// for (int i = 0, imax = prams.Length; i < imax; i++) {
// emit.ldarg1() // stack<= paramsValuesArray //push array
// .ldc_i4(i) // stack<= index push index
// .ldelem_ref(); // pop array, index and push array[index]
//
// var param = prams[i];
// var dataType = param.ParameterType;
//
// if (dataType.IsByRef)
// dataType = dataType.GetElementType();
//
// var tmp = emit.declocal(dataType);
// emit.unbox_any(dataType)
// .stloc(tmp)
// .ifbyref_ldloca_else_ldloc(tmp, param.ParameterType);
//
////v2
//
//
//// emit.unbox_any(dataType);
////
//// emit.declocal(dataType);
//// emit.stloc(i+localVarStart)
//// .ifbyref_ldloca_else_ldloc(i+localVarStart, param.ParameterType);
//
//
// }
//
// // perform the correct call (pushes the result)
// emit.callorvirt(method);
//
//
// if (!method.IsStatic && targetType.IsValueType)
// emit.ldarg0().ldloc0().box(targetType).stind_ref();
//
//
// for (int i = 0; i < prams.Length; i++) {
// var paramType = prams[i].ParameterType;
// if (paramType.IsByRef)
// {
// var byRefType = paramType.GetElementType();
// emit.ldarg1() // stack<= params[]
// .ldc_i4(i) // stack<= i
// .ldloc(i + localVarStart); // stack<= list[i+localVarStart]
// if (byRefType.IsValueType)
// emit.box(byRefType); // if ex. stack =(object)stack[top]
// emit.stelem_ref(); // // stack:paramsValuesArray[i]= list[i+localVarStart];
// }
// }
//
// if (method.ReturnType == typeof(void))
// emit.ldnull();
// else if (weaklyTyped)
// emit.ifvaluetype_box(method.ReturnType);
//
// emit.ret();
}