创建动态方法的弱委托(参考对象,对象[] ARR)



源代码: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();

    }

我正在使用的stuct的示例是vector3与set方法:

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

错误:

无效的programexception:无效的IL代码(包装器动态方法(对象:mc&lt;>(object&amp;,object [](:il_004f:调用0x00000009 裁判认为实际IL代码在emit.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;
}

我找到了解决方案。首先,尽管生成的方法是正确的,但不是通过设置参考。所以我尝试生成另一种方法。

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

    }

最新更新