Знайти приватне поле за допомогою Reflection?


228

З огляду на цей клас

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

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

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

Я зробив це з властивостями, де я шукав атрибут, але ніколи не приватне поле учасника.

Які обов'язкові прапори потрібно встановити для отримання приватних полів?


@Nescio: Чи можете ви розширити питання, чому ви б застосували такий підхід? ... переваги? Або просто перевагу? :)
— IАнотація

Відповіді:


279

Використання BindingFlags.NonPublicта BindingFlags.Instanceпрапори

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

11
Я міг змусити це працювати, лише надавши прапор "BindingFlags.Instance".
— Енді Макклуггедж,

1
Я зафіксував вашу відповідь. Інакше це занадто заплутано. Однак відповідь Абе Хайдебрехта була найповнішою.
— lubos hasko

2
Чудово працює - версія FYI VB.NET Me.GetType (). GetFields (Reflection.BindingFlags.NonPublic Or 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) не поверне null, якщо атрибут не знайдено, він просто поверне порожній масив.
— амнезія

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 із кількома додатками - це те, що ти отримуєш?
— Брайан Суїні

Я говорю про те, де 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 в моєму коді! Дивовижно!
— тайвань

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

Так, однак вам потрібно буде встановити ваші прапори прив'язки для пошуку приватних полів (якщо ви шукаєте члена поза екземпляром класу).

Потрібний прапор, який вам знадобиться: 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.