Znajdź prywatne pole z Reflection?

228

Biorąc pod uwagę tę klasę

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

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

Chcę znaleźć prywatny element _bar, który oznaczę atrybutem. Czy to jest możliwe?

Zrobiłem to z właściwościami, w których szukałem atrybutu, ale nigdy nie jest to pole prywatnego członka.

Jakie flagi wiążące muszę ustawić, aby uzyskać pola prywatne?

David Basarab
źródło
@Nescio: Czy możesz wyjaśnić, dlaczego chcesz przyjąć takie podejście? ...korzyści? A może po prostu preferencja? :)
IAbstract

Odpowiedzi:

279

Użyj BindingFlags.NonPublici BindingFlags.Instanceflagi

FieldInfo[] fields = myType.GetFields(
                         BindingFlags.NonPublic | 
                         BindingFlags.Instance);
Bob King
źródło
11
Mogłem tylko to uruchomić, podając również flagę wiązania „BindingFlags.Instance”.
Andy McCluggage
1
Naprawiłem twoją odpowiedź. W przeciwnym razie jest to zbyt mylące. Odpowiedź Abe Heidebrechta była jednak najbardziej kompletna.
lubos hasko
2
Działa świetnie - FYI VB.NET wersja Me.GetType (). GetFields (Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance)
gg.
2
Użycie flagi powiązania instancji jest możliwe tylko wtedy, gdy chcesz uzyskać metody instancji. Jeśli chcesz uzyskać prywatną metodę statyczną, możesz użyć (BindingFlags.NonPublic |
BindingFlags.Static
166

Możesz to zrobić tak jak w przypadku właściwości:

FieldInfo fi = typeof(Foo).GetField("_bar", BindingFlags.NonPublic | BindingFlags.Instance);
if (fi.GetCustomAttributes(typeof(SomeAttribute)) != null)
    ...
Abe Heidebrecht
źródło
8
Przepraszam za ekstremalne nekrologowanie, ale to mnie odrzuciło. GetCustomAttributes (Type) nie zwróci null, jeśli atrybut nie zostanie znaleziony, po prostu zwróci pustą tablicę.
amnezja
42

Uzyskaj wartość zmiennej prywatnej za pomocą Reflection:

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

Ustaw wartość zmiennej prywatnej za pomocą Reflection:

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

Gdzie objectForFooClass jest instancją niepustą dla typu klasy Foo.

Surija
źródło
Podobna odpowiedź opisuje łatwą w użyciu funkcję GetInstanceField(typeof(YourClass), instance, "someString") as string Jak uzyskać wartość pola prywatnego w C #?
Michael Freidgeim
24

Jedną z rzeczy, o których musisz wiedzieć, zastanawiając się nad prywatnymi członkami, jest to, że jeśli twoja aplikacja działa na średnim poziomie zaufania (jak na przykład, gdy pracujesz na współużytkowanym środowisku hostingowym), nie znajdzie ich - Opcja BindingFlags.NonPublic zostanie po prostu zignorowana.

jammycakes
źródło
jammycakes czy mógłbyś podać przykład współdzielonego środowiska hostingowego? myślę, że iis z wieloma aplikacjami jest tym, co dostajesz?
Brian Sweeney,
Mówię o tym, gdzie IIS jest zablokowany do częściowego zaufania na poziomie machine.config. Zazwyczaj znajdziesz to tylko na tanich i nieprzyjemnych udostępnianych planach hostingu w dzisiejszych czasach (takich, których już nie używam) - jeśli masz pełną kontrolę nad swoim serwerem, to nie będzie to naprawdę istotne, ponieważ pełne zaufanie jest domyślna.
jammycakes
18
typeof(MyType).GetField("fieldName", BindingFlags.NonPublic | BindingFlags.Instance)
Darren Kopp
źródło
Nie poznam nazwy pola. Chcę go znaleźć bez nazwy i kiedy ma na nim atrybut.
David Basarab,
Aby znaleźć nazwę pola, łatwo to zrobić w Visual Studio. Ustaw punkt przerwania dla zmiennej, wyświetl jej pola (w tym prywatne, zwykle rozpoczynane od m_fieldname). Zamień m_fieldname na powyższe polecenie.
Hao Nguyen
13

Ładna składnia z metodą rozszerzenia

Możesz uzyskać dostęp do dowolnego prywatnego pola dowolnego typu za pomocą takiego kodu:

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

W tym celu musisz zdefiniować metodę rozszerzenia, która wykona pracę za Ciebie:

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);
    }
}
Bruno Zell
źródło
1
Stary, to było IDEALNE, jeśli chodzi o dostęp do chronionej zmiennej bez narażania jej na NLua w moim kodzie! Niesamowite!
tayoung
6

Używam tej metody osobiście

if (typeof(Foo).GetFields(BindingFlags.NonPublic | BindingFlags.Instance).Any(c => c.GetCustomAttributes(typeof(SomeAttribute), false).Any()))
{ 
    // do stuff
}
sa_ddam213
źródło
6

Oto kilka metod rozszerzenia do prostego pobierania i ustawiania prywatnych pól i właściwości (właściwości z ustawiaczem):

przykład użycia:

    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

Kod:

    /// <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
źródło
4

Tak, jednak musisz ustawić flagi Binding, aby wyszukiwały pola prywatne (jeśli szukasz członka spoza instancji klasy).

Potrzebna flaga wiązania to: System.Reflection.BindingFlags.NonPublic

mmattax
źródło
2

Natknąłem się na to podczas wyszukiwania tego w google, więc zdaję sobie sprawę, że wpadam na stary post. Jednak GetCustomAttributes wymaga dwóch parametrów.

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

Drugi parametr określa, czy chcesz przeszukiwać hierarchię dziedziczenia

Artylerzysta
źródło