C# 反射调用具有 ref/Pointer 参数的方法

3
我想通过反射调用类的每个方法,但我不能使用指针作为引用来调用方法。通常情况下,我只需为找到的每个指针传递null,一切都会很好。问题在于当函数尝试访问我传递的指针时。指针将是对内存地址0的引用,这显然会导致我的程序立即崩溃。
因此,我需要传递一个指向有效内存地址的指针,但我不知道如何在运行时创建指针。
以下是代码的压缩版本:
class Program
{
    static void Main(string[] args)
    {
        var obj = new TestClass();
        var pointerMethod = obj.GetType().GetMethod("Test");
        var referenceMethod = obj.GetType().GetMethod("Test2");

        var pointerParameter = pointerMethod.GetParameters()[0];
        var referenceParameter = referenceMethod.GetParameters()[0];

        //Get the instance of the class that is referred by the pointer/reference
        var pointerInstance = Activator.CreateInstance(pointerParameter.ParameterType.GetElementType());
        var referenceInstance = Activator.CreateInstance(referenceParameter.ParameterType.GetElementType());

        //Call the method and "cast" the char instance into an pointer
        pointerMethod.Invoke(obj, new[] {pointerInstance.GetType().MakePointerType()});
        referenceMethod.Invoke(obj, new[] { referenceInstance.GetType().MakeByRefType() });
     }
}

还有一个TestClass:

public class TestClass
{
        unsafe public void Test(char* pointer)
        {

        }

        public void Test2(ref int reference)
        {

        }
}

我经常遇到异常"System.RuntimeType"无法转换为类型 'System.Char*' 奇怪的是,pointerInstance.GetType().MakePointerType()返回一个char*,这恰好是我需要传递给函数的...

2
你的方法声明无效,因为你没有提供参数类型。(ref是一个修饰符,不是一种类型。) 如果你能提供一个简短但完整的程序来演示问题,那将非常有帮助。此外,我建议你尝试使用仅仅一个ref参数或仅仅一个指针参数 - 否则你将不知道哪个出了问题... - Jon Skeet
我已经编辑了我的文章并压缩了代码。 - Djeurissen
但仍未包含一个简短而完整的程序,我可以简单地复制、粘贴、编译和运行。ref版本是否有效?如果是,请删除它 - 集中精力解决一个问题比展示两个问题更好。 - Jon Skeet
ref版本抛出相同的异常。程序已经完成,您只需要将它粘贴到主函数中即可...^^ - Djeurissen
请注意,您似乎试图将类型作为参数传递,而不是传递Invoke的第二个参数应该是要传递的参数,而不是参数的类型... - Jon Skeet
1
如果它不包含类和方法声明,那就不完整。为什么每个想要帮助你的人都需要做这个工作,当你可以只做一次呢?这真的不难,而且作为寻求帮助的人,你应该付出努力。 - Jon Skeet
1个回答

10

首先,让我们将其分为指针和ref - 它们的处理方式略有不同。

不清楚您希望该方法接收哪个指针值 - 特别是您传递给Invoke的值是类型而不是类型的实例 - 但您应该使用Pointer.Box

using System.Reflection;

class TestPointer
{    
    unsafe static void Main()
    {
        char c = 'x';
        char* p = &c;
        object boxedPointer = Pointer.Box(p, typeof(char*));

        var method = typeof(TestPointer).GetMethod("Foo");
        method.Invoke(null, new[] { boxedPointer });
        Console.WriteLine("After call, c = {0}", c);
    }

    public static unsafe void Foo(char *pointer)
    {
        Console.WriteLine("Received: {0}", *pointer);
        *pointer = 'y';
    }
}

输出:

Received: x
After call, c = y

对于ref参数来说,稍微简单一些 - 你只需要允许普通装箱,然后修改参数数组:

对于ref参数,可以进行普通的装箱操作,并修改参数数组。

using System.Reflection;

class TestRef
{    
    unsafe static void Main()
    {
        var method = typeof(TestRef).GetMethod("Foo");
        var args = new object[] { 10 };
        method.Invoke(null, args);
        Console.WriteLine("After call, args[0] = {0}", args[0]);
    }

    public static unsafe void Foo(ref int x)
    {
        Console.WriteLine("Received: {0}", x);
        x = 20;
    }
}

2
@leppie:我今天之前从未见过它 :) - Jon Skeet
非常感谢。我完全忘记了将类型作为参数传递。但我仍然有一个问题,如果我不知道我有哪种类型的指针怎么办?就像在我的示例中,我只有一个表示类型的对象。 - Djeurissen
@Djeurissen:如果你连类型都不知道,你指望从哪里获取值呢? - Jon Skeet
@Djeurissen:那么你希望从哪里获取 - Jon Skeet
@Djeurissen:现在,关于指针本身的值——不确定你所说的“指向引用”的意思。你不能从无中创建指针……我真的认为你应该退后一步,考虑一下你想要实现什么……目前完全不明显。 - Jon Skeet
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接