【问题标题】:Get memory address of field using reflection (C#)使用反射(C#)获取字段的内存地址
【发布时间】:2018-06-09 00:36:37
【问题描述】:

在.Net Core中,当编译时父对象未知时,如何获取原始字段的内存地址(IntPtr)?

如果对象类是一个结构体并且是 blittable,那么我可以使用 Marshel.Offset,但不幸的是它不是。

下面的代码说明了我正在尝试做的事情

    class Example   // this class is defined in a different assembly and is not known at compile time
    {
        public double foo;
    }

    static void Main(string[] args)
    {
        Example obj = new Example();
        obj.foo = 123;

        var hdl = GCHandle.Alloc(obj, GCHandleType.Pinned);
        IntPtr foo_add = GetAddressOf(obj, "foo");

        hdl.Free();
    }

    static IntPtr GetAddressOf(object pinned_object, string field_name)
    {
        FieldInfo field = pinned_object.GetType().GetField(field_name);
        return field.GetFieldAddress(pinned_object); // Unfortunatly this function does not exist, what are the alternatives?
    }

替代方案question 着眼于获取对象的内存地址,这可以使用 GCHandle.Alloc 完成,但不适用于原语。

【问题讨论】:

  • 显然甚至接近重复。 Op询问原始字段的地址。另一个问题是对象的地址。

标签: c# .net-core


【解决方案1】:

以下答案描述了如何使用 C# 7.0 中的“ref return”功能获取对字段的托管引用:

https://stackoverflow.com/a/45046664/425678

基于这个出色的答案,我们可以修改函数create_refgetter 以获得非托管指针。这利用了使用泛型类型时需要的鲜为人知的函数 __makeref (detailed explanation)

然后这个函数被封装在函数GetAddressOf 中,它允许在不指定泛型类型的情况下调用。

    static IntPtr GetAddressOf(object pinned_object, string field_name)
    {
        var fi_val = pinned_object.GetType().GetField(field_name);
        var mi_all = typeof(Program).GetMethod("create_refgetter", BindingFlags.Static | BindingFlags.Public);

        var mi_generic = mi_all.MakeGenericMethod(pinned_object.GetType(), fi_val.FieldType);
        var ptr = (IntPtr) mi_generic.Invoke(null, new object[] {pinned_object, field_name });

        return ptr;
    }

    https://stackoverflow.com/a/45046664/425678

    public delegate ref U RefGetter<T, U>(T obj);
    public static IntPtr create_refgetter<T, U>(object obj,string s_field)
    {
        const BindingFlags bf = BindingFlags.NonPublic |
                                BindingFlags.Public |
                                BindingFlags.Instance |
                                BindingFlags.DeclaredOnly;

        var fi = typeof(T).GetField(s_field, bf);
        if (fi == null)
            throw new MissingFieldException(typeof(T).Name, s_field);

        var s_name = "__refget_" + typeof(T).Name + "_fi_" + fi.Name;

        // workaround for using ref-return with DynamicMethod:
        //   a.) initialize with dummy return value
        var dm = new DynamicMethod(s_name, typeof(U), new[] { typeof(T) }, typeof(T), true);

        //   b.) replace with desired 'ByRef' return value
        dm.GetType().GetField("m_returnType", bf).SetValue(dm, typeof(U).MakeByRefType());

        var il = dm.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Ldflda, fi);
        il.Emit(OpCodes.Ret);

        RefGetter<T, U> ref_getter = (RefGetter<T, U>)dm.CreateDelegate(typeof(RefGetter<T, U>));

        unsafe
        {
            TypedReference t_ref = __makeref(ref_getter((T)obj));

            IntPtr ptr = *((IntPtr*)&t_ref);
            return ptr;
        }
    }
}

示例:

class Example   // this class is defined in a different assembly and is not known at compile time
{
    public double foo;
}

static void Main(string[] args)
{
    Example obj = new Example();
    obj.foo = 123;

    var hdl = GCHandle.Alloc(obj);

    unsafe
    {
        IntPtr foo_add = GetAddressOf(obj, "foo");

        double* ptr = (double*) foo_add;
        Console.WriteLine("Current Value: {0}", *ptr);

        *ptr = 4;
        Console.WriteLine("Updated Value: {0}", obj.foo);
    }

    hdl.Free();

    Console.ReadLine();
}

示例输出:

Current Value: 123
Updated Value: 4

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多