如何在C#中解决错误"反射调用中不支持ByRef返回值"?

Les*_*Les 6 c# reflection ref invoke

我有一个由第三方提供的.Net库.我对其中一个类进行了反射,并找到了一个成员方法.签名是......

Byte& FooBar()
Run Code Online (Sandbox Code Playgroud)

所以,我想通过反射调用这个方法并获得异常"反射调用中不支持ByRef返回值".

这是我试过的......

        var strm = new TheirClass();
        var t = strm.GetType();
        var ms = t.GetMembers(
                    BindingFlags.Static|BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
        foreach (var m in ms)
        {
            Debug.WriteLine(String.Format("Name: {0}: {1}", m.Name, m.ToString()));
            // ...
            // Name: FooBar:  Byte& FooBar()
            // ...
        }
        var meth = t.GetMethod("FooBar");
        object returnValue = meth.Invoke(strm, new object[] {  });  //throw exception
Run Code Online (Sandbox Code Playgroud)

我已经尝试提供参数,如使用ref参数调用函数,但这没有任何区别.

我想在C#中解决这个异常.

小智 5

根据评论:这里是如何从CIL完成的,可以从C#生成.

我希望使用a DynamicMethod,但是如果没有在运行时创建自定义委托类型,我无法使其工作,所以我需要使用AssemblyBuilder.

using System;
using System.Reflection;
using System.Reflection.Emit;

public delegate void CallBadFunction(Delegate d, Callback c);
public delegate void Callback(ref int i);

static class Program
{
    static int i;
    static object BadMethod()
    {
        return i;
    }

    static MethodInfo GetBadMethod()
    {
        return typeof(Program).GetMethod("BadMethod", BindingFlags.Static | BindingFlags.NonPublic);
    }

    static void Main()
    {
        var badMethod = GetBadMethod();

        var assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("-"), AssemblyBuilderAccess.Run);
        var module = assembly.DefineDynamicModule("-");

        var badDelegate = module.DefineType("BadDelegateType", TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Sealed, typeof(MulticastDelegate));
        var badDelegateCtor = badDelegate.DefineConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, CallingConventions.Standard, new Type[] { typeof(object), typeof(IntPtr) });
        badDelegateCtor.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);
        var badDelegateInvoke = badDelegate.DefineMethod("Invoke", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.HideBySig, typeof(int).MakeByRefType(), Type.EmptyTypes);
        badDelegateInvoke.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);
        var badDelegateType = badDelegate.CreateType();

        var method = module.DefineGlobalMethod("-", MethodAttributes.Public | MethodAttributes.Static, typeof(void), new[] { typeof(Delegate), typeof(Callback) });
        var il = method.GetILGenerator();
        il.Emit(OpCodes.Ldarg_1);
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Castclass, badDelegate);
        il.Emit(OpCodes.Callvirt, badDelegateInvoke);
        il.Emit(OpCodes.Callvirt, typeof(Callback).GetMethod("Invoke"));
        il.Emit(OpCodes.Ret);
        module.CreateGlobalFunctions();

        var callBadFunction = (CallBadFunction)Delegate.CreateDelegate(typeof(CallBadFunction), module.GetMethod("-"));
        callBadFunction(badMethod.CreateDelegate(badDelegateType), (ref int i) =>
        {
            i++;
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

编译完这个程序后,使用ILDASM对它进行反汇编,然后替换它BadMethod的定义

.method private hidebysig static int32&
        BadMethod() cil managed
{
  ldsflda     int32 Program::i
  ret
}
Run Code Online (Sandbox Code Playgroud)

这将它变成一个返回的函数int32&,然后下面的代码将设法调用它.C#允许int32&类型的唯一位置是函数参数(ref int),所以为了使结果可用,我使用了一个回调函数,它传递了返回值BadMethod.