Finden Sie ein privates Feld mit Reflection?

228

Angesichts dieser Klasse

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

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

Ich möchte das private Element _bar finden, das ich mit einem Attribut markieren werde. Ist das möglich?

Ich habe dies mit Eigenschaften gemacht, bei denen ich nach einem Attribut gesucht habe, aber niemals nach einem privaten Mitgliedsfeld.

Welche Bindungsflags muss ich setzen, um die privaten Felder abzurufen?

David Basarab
quelle
@Nescio: Können Sie näher erläutern, warum Sie diesen Ansatz wählen würden? ...die Vorteile? Oder einfach lieber? :)
IAbstract

Antworten:

279

Verwendung BindingFlags.NonPublicund BindingFlags.InstanceFlaggen

FieldInfo[] fields = myType.GetFields(
                         BindingFlags.NonPublic | 
                         BindingFlags.Instance);
Bob King
quelle
11
Ich konnte dies nur zum Laufen bringen, indem ich auch das Bindungsflag "BindingFlags.Instance" bereitstellte.
Andy McCluggage
1
Ich habe deine Antwort korrigiert. Sonst ist es zu verwirrend. Abe Heidebrechts Antwort war jedoch die vollständigste.
Lubos Hasko
2
Funktioniert hervorragend - FYI VB.NET-Version Me.GetType (). GetFields (Reflection.BindingFlags.NonPublic oder Reflection.BindingFlags.Instance)
gg.
2
Die Verwendung des Instanzbindungsflags ist nur möglich, wenn Sie Instanzmethoden abrufen möchten. Wenn Sie eine private statische Methode erhalten möchten, können Sie (BindingFlags.NonPublic | BindingFlags.Static)
ksun
166

Sie können es genauso machen wie mit einer Immobilie:

FieldInfo fi = typeof(Foo).GetField("_bar", BindingFlags.NonPublic | BindingFlags.Instance);
if (fi.GetCustomAttributes(typeof(SomeAttribute)) != null)
    ...
Abe Heidebrecht
quelle
8
Entschuldigung für das extreme Nekro-Posting, aber das hat mich umgehauen. GetCustomAttributes (Type) gibt nicht null zurück, wenn das Attribut nicht gefunden wird, sondern gibt einfach ein leeres Array zurück.
Amnesie
42

Ermitteln Sie den Wert der privaten Variablen mithilfe von Reflection:

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

Stellen Sie den Wert für die private Variable mit Reflection ein:

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

Wobei objectForFooClass eine Nicht-Null-Instanz für den Klassentyp Foo ist.

Suriya
quelle
Eine ähnliche Antwort beschreibt eine benutzerfreundliche Funktion. GetInstanceField(typeof(YourClass), instance, "someString") as string Wie erhalte ich den Wert eines privaten Feldes in C #?
Michael Freidgeim
24

Eine Sache, die Sie beachten müssen, wenn Sie über private Mitglieder nachdenken, ist, dass Ihre Anwendung, wenn sie in mittlerer Vertrauenswürdigkeit ausgeführt wird (z. B. wenn Sie in einer gemeinsam genutzten Hosting-Umgebung ausgeführt werden), diese nicht findet - die Die Option BindingFlags.NonPublic wird einfach ignoriert.

Marmeladenkuchen
quelle
jammycakes könnten Sie bitte ein Beispiel für eine Shared Hosting-Umgebung geben? Ich denke, iis mit mehreren Apps ist das, was Sie bekommen?
Brian Sweeney
Ich spreche darüber, wo IIS auf der Ebene machine.config für teilweise Vertrauenswürdigkeit gesperrt ist. Normalerweise finden Sie dies heutzutage nur bei billigen und fiesen Shared-Web-Hosting-Plänen (wie ich sie nicht mehr verwende). Wenn Sie die volle Kontrolle über Ihren Server haben, ist dies nicht wirklich relevant, da volles Vertrauen das ist Standard.
Jammycakes
18
typeof(MyType).GetField("fieldName", BindingFlags.NonPublic | BindingFlags.Instance)
Darren Kopp
quelle
Ich werde den Namen des Feldes nicht kennen. Ich möchte es ohne den Namen finden und wenn das Attribut darauf ist.
David Basarab
Um den Feldnamen zu finden, ist dies in Visual Studio ganz einfach. Setzen Sie den Haltepunkt für die Variable und zeigen Sie ihre Felder an (einschließlich des privaten, normalerweise mit m_fieldname begonnenen). Ersetzen Sie diesen m_fieldname durch den obigen Befehl.
Hao Nguyen
13

Schöne Syntax mit Erweiterungsmethode

Sie können mit folgendem Code auf jedes private Feld eines beliebigen Typs zugreifen:

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

Dazu müssen Sie eine Erweiterungsmethode definieren, die die Arbeit für Sie erledigt:

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
quelle
1
Alter, das war PERFEKT für den Zugriff auf eine geschützte Variable, ohne sie in meinem Code NLua auszusetzen! Genial!
Tayoung
6

Ich benutze diese Methode persönlich

if (typeof(Foo).GetFields(BindingFlags.NonPublic | BindingFlags.Instance).Any(c => c.GetCustomAttributes(typeof(SomeAttribute), false).Any()))
{ 
    // do stuff
}
sa_ddam213
quelle
6

Hier sind einige Erweiterungsmethoden zum einfachen Abrufen und Festlegen privater Felder und Eigenschaften (Eigenschaften mit Setter):

Anwendungsbeispiel:

    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

Code:

    /// <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
quelle
4

Ja, Sie müssen jedoch Ihre Bindungsflags setzen, um nach privaten Feldern zu suchen (wenn Sie nach dem Mitglied außerhalb der Klasseninstanz suchen).

Das Bindungsflag, das Sie benötigen, lautet: System.Reflection.BindingFlags.NonPublic

mmattax
quelle
2

Ich bin darauf gestoßen, als ich auf Google danach gesucht habe, und habe festgestellt, dass ich einen alten Beitrag stoße. Für GetCustomAttributes sind jedoch zwei Parameter erforderlich.

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

Der zweite Parameter gibt an, ob Sie die Vererbungshierarchie durchsuchen möchten oder nicht

Kanonier
quelle