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


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 Не могли бы вы привести пример среды общего хостинга? я думаю, что с несколькими приложениями, что вы получаете?
— Брайан Суини

Я говорю о том, где 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
}

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;
        }
    }

4

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

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


2

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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.