Trova un campo privato con Reflection?


228

Data questa classe

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

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

Voglio trovare l'elemento privato _bar che segnerò con un attributo. È possibile?

L'ho fatto con proprietà in cui ho cercato un attributo, ma mai un campo membro privato.

Quali sono i flag di associazione che devo impostare per ottenere i campi privati?


@Nescio: puoi espandere il motivo per cui dovresti adottare questo approccio? ...i benefici? O semplicemente preferenza? :)
Estratto

Risposte:


279

Usa BindingFlags.NonPublice BindingFlags.Instancebandiere

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

11
Ho potuto farlo funzionare solo fornendo anche il flag di associazione "BindingFlags.Instance".
Andy McClubag,

1
Ho risolto la tua risposta. Altrimenti è troppo confuso. La risposta di Abe Heidebrecht è stata la più completa.
Lubos Hasko,

2
Funziona alla grande - FYI VB.NET versione Me.GetType (). GetFields (Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance)
gg.

2
L'uso del flag di associazione dell'istanza è solo se si desidera ottenere metodi di istanza. Se si desidera ottenere un metodo statico privato, è possibile utilizzare (BindingFlags.NonPublic | BindingFlags.Static)
ksun,

166

Puoi farlo proprio come con una proprietà:

FieldInfo fi = typeof(Foo).GetField("_bar", BindingFlags.NonPublic | BindingFlags.Instance);
if (fi.GetCustomAttributes(typeof(SomeAttribute)) != null)
    ...

9
Ci scusiamo per il necro-post estremo, ma questo mi ha buttato via. GetCustomAttributes (Type) non restituirà null se l'attributo non viene trovato, restituisce semplicemente un array vuoto.
amnesia,

42

Ottieni il valore della variabile privata usando Reflection:

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

Imposta il valore per la variabile privata usando Reflection:

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

Dove objectForFooClass è un'istanza non nulla per il tipo di classe Foo.


Una risposta simile descrive una funzione facile da usare GetInstanceField(typeof(YourClass), instance, "someString") as string Come ottenere il valore del campo privato in C #?
Michael Freidgeim,

24

Una cosa di cui devi essere consapevole quando rifletti sui membri privati ​​è che se la tua applicazione è in esecuzione con un livello di attendibilità medio (come, ad esempio, quando esegui un ambiente di hosting condiviso), non li troverà: L'opzione BindingFlags.NonPublic verrà semplicemente ignorata.


jammycakes potresti per favore dare un esempio di ambiente di hosting condiviso? sto pensando che sto con più app è quello che stai ottenendo?
Brian Sweeney,

Sto parlando di dove IIS è bloccato per la fiducia parziale a livello di machine.config. Di solito lo trovi solo su piani di web hosting condivisi a buon mercato in questi giorni (cose del genere che non uso più) - se hai il pieno controllo del tuo server, allora non sarà davvero rilevante poiché la fiducia completa è il predefinito.
Jammycakes,

18
typeof(MyType).GetField("fieldName", BindingFlags.NonPublic | BindingFlags.Instance)

Non saprò il nome del campo. Voglio trovarlo senza il nome e quando l'attributo è su di esso.
David Basarab,

Per trovare il nome del campo, è facile farlo in Visual Studio. Imposta il punto di interruzione sulla variabile, visualizza i suoi campi (incluso il privato, di solito inizia con m_fieldname). Sostituisci quel m_fieldname nel comando sopra.
Hao Nguyen,

13

Bella sintassi con metodo di estensione

Puoi accedere a qualsiasi campo privato di tipo arbitrario con un codice come questo:

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

Per questo è necessario definire un metodo di estensione che farà il lavoro per te:

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
Amico, questo era PERFETTO per accedere a una variabile protetta senza esporla a NLua nel mio codice! Eccezionale!
tayoung,

6

Uso questo metodo personalmente

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

6

Ecco alcuni metodi di estensione per ottenere e impostare semplici campi e proprietà private (proprietà con setter):

esempio di utilizzo:

    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

Codice:

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

Sì, tuttavia dovrai impostare i flag di Binding per cercare campi privati ​​(se stai cercando il membro al di fuori dell'istanza della classe).

Il flag di rilegatura che ti servirà è: System.Reflection.BindingFlags.NonPublic


2

Mi sono imbattuto in questo mentre cercavo questo su Google, quindi mi rendo conto che sto sbattendo un vecchio post. Tuttavia GetCustomAttributes richiede due parametri.

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

Il secondo parametro consente di specificare se si desidera cercare nella gerarchia dell'ereditarietà

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.