访问私有字段

20

能否获取或设置私有字段?

我想获取System.Guid.c。是否有方法可以访问它,还是应该将代码从结构中复制并将字段设置为公共的?


已经更改了,但它只是 System.Guid 而已。 - godzcheater
1
可能是使用反射查找私有字段?的重复问题。 - Chris Diver
4个回答

30

你可以像Quantic Programming建议的那样使用反射。

var guid = Guid.NewGuid();
var field= typeof (Guid).GetField("_c", BindingFlags.NonPublic |BindingFlags.GetField | BindingFlags.Instance);
var value = field.GetValue(guid);

如果你可以先将GUID转换为字节数组,那么我建议使用以下方式:

var guid = Guid.NewGuid();
var c = BitConverter.ToInt16(guid.ToByteArray(), 6);

后一种方法避免了使用反射。

编辑

你提到需要能够设置值,但仍然可以避免使用反射:

var guid = Guid.NewGuid();
var guidBytes = guid.ToByteArray();

// get value
var c = BitConverter.ToInt16(guidBytes, 6);

// set value
Buffer.BlockCopy(BitConverter.GetBytes(c), 0, guidBytes, 6, sizeof(Int16));
var modifiedGuid = new Guid(guidBytes);

谢谢,我认为除非使用反射会有负面影响,否则应该使用第一个。 - godzcheater
3
根据drf提到的,使用反射访问私有成员通常是不好的做法。您正在使用类的内部来访问数据。在可能的情况下,应始终尝试遵循公共API。反射私有成员将需要完全信任的环境,并且有可能成为脆弱的代码,因为类的内部可能随时间而变化。 - Chris Baxter

6

您可以编写一个扩展方法来获取任何类型的任何私有字段:

public static T GetFieldValue<T>(this object obj, string name) {
    var field = obj.GetType().GetField(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
    return (T)field?.GetValue(obj);
}

然后访问任意类型的私有字段:

Guid id = Guid.NewGuid();
Int16 c = id.GetFieldValue<Int16>("_c");

6
你应该尝试使用System.Reflection。以下是一个示例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace AccessPrivateField
{
    class foo
    {
        public foo(string str)
        {
            this.str = str;
        }
        private string str;
        public string Get()
        {
            return this.str;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            foo bar = new foo("hello");
            Console.WriteLine(bar.Get());
            typeof(foo).GetField("str", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(bar, "changed");
            Console.WriteLine(bar.Get());
            //output:
            //hello
            //changed
        }
    }
}

1
请注意,如果您正在运行Silverlight,则无法访问私有成员。您没有指定您正在使用它,但以防万一提一下。 - Chris Sinclair
为什么不行?你是指无法访问Silverlight的内部数据,还是在Silverlight下根本就做不到? - Mark Segal
2
使用Silverlight,运行时会通过反射防止您访问非公共数据,因为这是一个安全问题。例如,一些文件I/O代码在运行时中,但通常只能通过特殊的托管方式访问它,这不会违反安全问题。如果您可以使用反射来深入隐藏的文件I/O,则仅通过浏览恶意Silverlight页面就可以随意擦除/读取计算机文件。 - Chris Sinclair
哦,一个安全问题。谢谢! - Mark Segal

3

虽然可以使用反射来实现此操作,但从System.Guid.ToByteArray()中检索c可能更容易。

byte[] guid = guid.ToByteArray();
short c = (short)((guid[7] << 8) | guid[6]);

由于这种方法使用公共和文档化的方法,因此它在版本之间变化较少。 (一般来说,应避免依赖私有实现细节,因为这些细节可能会随着版本的更改而发生变化。)


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