使用反射如何获取私有字段?

277

考虑到这个类

class Foo
{
    // Want to find _bar with reflection
    [SomeAttribute]
    private string _bar;

    public string BigBar
    {
        get { return this._bar; }
    }
}

我想找到我将用属性标记的私有项目_bar。这是可能的吗?

我已经在属性中执行了此操作,其中我查找了一个属性,但从未找过私有成员字段。

我需要设置哪些绑定标志才能获取私有字段?

11个回答

350

使用 BindingFlags.NonPublicBindingFlags.Instance 标志。

FieldInfo[] fields = myType.GetFields(
                         BindingFlags.NonPublic | 
                         BindingFlags.Instance);

16
我只能通过提供“BindingFlags.Instance”绑定标志才能使这个工作。 - Andy McCluggage
2
我已经修正了你的答案。否则太令人困惑了。不过,Abe Heidebrecht的答案是最完整的。 - lubos hasko
2
运行良好 - FYI VB.NET 版本Me.GetType().GetFields(Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance) - gg.
3
只有在需要获取实例方法时才使用"instance binding flag"。如果要获取私有静态方法,可以使用(BindingFlags.NonPublic | BindingFlags.Static)。 - ksun
BindingFlags.Instance每次在使用反射访问非公共成员时都会让我犯错。如果您使用Default=0 BindingFlags调用GetFields(),则会自动获取实例成员。但是当您显式设置绑定标志时,它会删除实例成员的默认包含。这毫无意义。标志枚举应该是可添加的。 - Leaky

181

你可以像处理属性一样来做:

FieldInfo fi = typeof(Foo).GetField("_bar", BindingFlags.NonPublic | BindingFlags.Instance);
if (fi.GetCustomAttribute(typeof(SomeAttribute)) != null)
    ...

14
抱歉长时间未回,但这让我有些困惑。如果找不到该属性,GetCustomAttributes(Type)方法将不会返回null,而是返回一个空数组。请原谅我的冒昧。 - amnesia

66

使用反射获取私有变量的值:

var _barVariable = typeof(Foo).GetField("_bar", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(objectForFooClass);
使用反射为私有变量设置值:
typeof(Foo).GetField("_bar", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(objectForFoocClass, "newValue");

objectForFooClass是一个非空的Foo类实例。


类似的答案描述了易于使用的函数 GetInstanceField(typeof(YourClass), instance, "someString") as string 如何在C#中获取私有字段的值? - Michael Freidgeim

42

使用扩展方法来优化语法

您可以通过以下代码访问任意类型的私有字段:

Foo foo = new Foo();
string c = foo.GetFieldValue<string>("_bar");

为此,您需要定义一个扩展方法来为您完成工作:
public static class ReflectionExtensions {
    public static T GetFieldValue<T>(this object obj, string name) {
        // Set the flags so that private and public fields from instances will be found
        var bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
        var field = obj.GetType().GetField(name, bindingFlags);
        return (T)field?.GetValue(obj);
    }
}

1
老兄,这个完美地实现了在我的代码中访问受保护变量而不暴露给 NLua!真是太好了! - tayoung

26

当您回顾私有成员时需要注意的一点是:如果您的应用程序在中等信任级别下运行(例如,当您在共享托管环境中运行时),它将无法找到它们 - BindingFlags.NonPublic选项将被忽略。


Jammycakes,您能否举个共享托管环境的例子?我在想您是指具有多个应用程序的IIS? - Brian Sweeney
1
我在谈论的是IIS在machine.config级别上被锁定为部分信任的情况。这种情况通常只出现在廉价而糟糕的共享Web托管计划中(我现在已不再使用此类服务)——如果您完全掌控自己的服务器,则这将不会是一个问题,因为完全信任是默认设置。 - jammycakes

19
typeof(MyType).GetField("fieldName", BindingFlags.NonPublic | BindingFlags.Instance)

我不知道字段的名称。我想在没有名称的情况下找到它,并且当属性在其上时。 - David Basarab
要查找字段名称,在Visual Studio中很容易实现。在变量上设置断点,查看其字段(包括私有字段,通常以m_fieldname开头)。将该m_fieldname替换为上述命令中的相应名称即可。 - Hao Nguyen

7

这里有一些用于简单获取和设置私有字段和具有 setter 的属性的扩展方法:

使用示例:

    public class Foo
    {
        private int Bar = 5;
    }

    var targetObject = new Foo();
    var barValue = targetObject.GetMemberValue("Bar");//Result is 5
    targetObject.SetMemberValue("Bar", 10);//Sets Bar to 10

代码:

    /// <summary>
    /// Extensions methos for using reflection to get / set member values
    /// </summary>
    public static class ReflectionExtensions
    {
        /// <summary>
        /// Gets the public or private member using reflection.
        /// </summary>
        /// <param name="obj">The source target.</param>
        /// <param name="memberName">Name of the field or property.</param>
        /// <returns>the value of member</returns>
        public static object GetMemberValue(this object obj, string memberName)
        {
            var memInf = GetMemberInfo(obj, memberName);

            if (memInf == null)
                throw new System.Exception("memberName");

            if (memInf is System.Reflection.PropertyInfo)
                return memInf.As<System.Reflection.PropertyInfo>().GetValue(obj, null);

            if (memInf is System.Reflection.FieldInfo)
                return memInf.As<System.Reflection.FieldInfo>().GetValue(obj);

            throw new System.Exception();
        }

        /// <summary>
        /// Gets the public or private member using reflection.
        /// </summary>
        /// <param name="obj">The target object.</param>
        /// <param name="memberName">Name of the field or property.</param>
        /// <returns>Old Value</returns>
        public static object SetMemberValue(this object obj, string memberName, object newValue)
        {
            var memInf = GetMemberInfo(obj, memberName);


            if (memInf == null)
                throw new System.Exception("memberName");

            var oldValue = obj.GetMemberValue(memberName);

            if (memInf is System.Reflection.PropertyInfo)
                memInf.As<System.Reflection.PropertyInfo>().SetValue(obj, newValue, null);
            else if (memInf is System.Reflection.FieldInfo)
                memInf.As<System.Reflection.FieldInfo>().SetValue(obj, newValue);
            else
                throw new System.Exception();

            return oldValue;
        }

        /// <summary>
        /// Gets the member info
        /// </summary>
        /// <param name="obj">source object</param>
        /// <param name="memberName">name of member</param>
        /// <returns>instanse of MemberInfo corresponsing to member</returns>
        private static System.Reflection.MemberInfo GetMemberInfo(object obj, string memberName)
        {
            var prps = new System.Collections.Generic.List<System.Reflection.PropertyInfo>();

            prps.Add(obj.GetType().GetProperty(memberName,
                                               System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance |
                                               System.Reflection.BindingFlags.FlattenHierarchy));
            prps = System.Linq.Enumerable.ToList(System.Linq.Enumerable.Where( prps,i => !ReferenceEquals(i, null)));
            if (prps.Count != 0)
                return prps[0];

            var flds = new System.Collections.Generic.List<System.Reflection.FieldInfo>();

            flds.Add(obj.GetType().GetField(memberName,
                                            System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance |
                                            System.Reflection.BindingFlags.FlattenHierarchy));

            //to add more types of properties

            flds = System.Linq.Enumerable.ToList(System.Linq.Enumerable.Where(flds, i => !ReferenceEquals(i, null)));

            if (flds.Count != 0)
                return flds[0];

            return null;
        }

        [System.Diagnostics.DebuggerHidden]
        private static T As<T>(this object obj)
        {
            return (T)obj;
        }
    }

6
我个人使用这种方法
if (typeof(Foo).GetFields(BindingFlags.NonPublic | BindingFlags.Instance).Any(c => c.GetCustomAttributes(typeof(SomeAttribute), false).Any()))
{ 
    // do stuff
}

4

是的,但是您需要设置绑定标志来搜索私有字段(如果要查找类实例外的成员)。

您需要的绑定标志是:System.Reflection.BindingFlags.NonPublic


3

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