Найти личное поле с отражением?

228

Учитывая этот класс

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

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

Я хочу найти закрытый элемент _bar, который я отмечу атрибутом. Это возможно?

Я сделал это со свойствами, в которых я искал атрибут, но никогда не закрывал частное поле.

Какие флаги привязки мне нужно установить, чтобы получить приватные поля?

Дэвид Басараб
источник
@Nescio: Можете ли вы рассказать, почему вы выбрали такой подход? ...выгоды? Или просто предпочтение? :)
IAbstract

Ответы:

279

Использование BindingFlags.NonPublicи BindingFlags.Instanceфлаги

FieldInfo[] fields = myType.GetFields(
                         BindingFlags.NonPublic | 
                         BindingFlags.Instance);
Боб Кинг
источник
11
Я мог только заставить это работать, также предоставляя флаг привязки "BindingFlags.Instance".
Энди МакКлагжаг
1
Я исправил ваш ответ. В противном случае это слишком запутанно. Ответ Абэ Хайдебрехта был самым полным, хотя.
Любос Хаско
2
Прекрасно работает - FYI VB.NET версия Me.GetType (). GetFields (Reflection.BindingFlags.NonPublic или Reflection.BindingFlags.Instance)
gg.
2
Использование флага привязки экземпляра возможно только в том случае, если вы хотите получить методы экземпляра. Если вы хотите получить приватный статический метод, который вы можете использовать (BindingFlags.NonPublic | BindingFlags.Static)
ksun
166

Вы можете сделать это так же, как со свойством:

FieldInfo fi = typeof(Foo).GetField("_bar", BindingFlags.NonPublic | BindingFlags.Instance);
if (fi.GetCustomAttributes(typeof(SomeAttribute)) != null)
    ...
Абе Хайдебрехт
источник
9
Извините за крайнее некропостинг, но это отбросило меня. GetCustomAttributes (Type) не будет возвращать ноль, если атрибут не найден, он просто возвращает пустой массив.
амнезия
42

Получить значение закрытой переменной, используя Reflection:

var _barVariable = typeof(Foo).GetField("_bar", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(objectForFooClass);

Установите значение для приватной переменной, используя Reflection:

typeof(Foo).GetField("_bar", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(objectForFoocClass, "newValue");

Где objectForFooClass - ненулевой экземпляр для типа класса Foo.

Сурия
источник
Подобный ответ описывает простую в использовании функцию GetInstanceField(typeof(YourClass), instance, "someString") as string Как получить значение частного поля в C #?
Майкл Фрейдгейм
24

Когда вы размышляете о частных участниках, вам нужно знать одну вещь: если ваше приложение работает со средним доверием (как, например, когда вы работаете в среде общего хостинга), оно не найдет их - Опция BindingFlags.NonPublic будет просто проигнорирована.

jammycakes
источник
jammycakes Не могли бы вы привести пример среды общего хостинга? я думаю, что с несколькими приложениями, что вы получаете?
Брайан Суини
Я говорю о том, где IIS заблокирован для частичного доверия на уровне machine.config. Обычно это можно найти только на дешевых и неприятных планах общего веб-хостинга (таких, которые я больше не использую) - если у вас полный контроль над сервером, это не будет иметь большого значения, поскольку полное доверие дефолт.
джеммикэйс
18
typeof(MyType).GetField("fieldName", BindingFlags.NonPublic | BindingFlags.Instance)
Даррен Копп
источник
Я не буду знать название поля. Я хочу найти его без имени и когда атрибут на нем.
Дэвид Басараб
Чтобы найти имя поля, это легко сделать в Visual Studio. Установите точку останова на переменную, просмотрите ее поля (включая приватные, обычно начинающиеся с m_fieldname). Замените это имя m_field на команду выше.
Хао Нгуен,
13

Хороший синтаксис с методом расширения

Вы можете получить доступ к любому закрытому полю произвольного типа с помощью кода, подобного следующему:

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
6

Я использую этот метод лично

if (typeof(Foo).GetFields(BindingFlags.NonPublic | BindingFlags.Instance).Any(c => c.GetCustomAttributes(typeof(SomeAttribute), false).Any()))
{ 
    // do stuff
}
sa_ddam213
источник
6

Вот некоторые методы расширения для простого получения и установки приватных полей и свойств (свойства с установщиком):

пример использования:

    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;
        }
    }
epsi1on
источник
4

Да, однако вам нужно будет установить флаги Binding для поиска приватных полей (если вы ищете член за пределами экземпляра класса).

Необходимый флаг привязки: System.Reflection.BindingFlags.NonPublic

mmattax
источник
2

Я столкнулся с этим во время поиска этого в Google, так что я понимаю, что наталкиваюсь на старый пост. Однако GetCustomAttributes требует двух параметров.

typeof(Foo).GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
.Where(x => x.GetCustomAttributes(typeof(SomeAttribute), false).Length > 0);

Второй параметр указывает, хотите ли вы искать иерархию наследования

пулеметчик
источник