Знайти приватне поле за допомогою 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.