Trouver un domaine privé avec Reflection?

228

Compte tenu de cette classe

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

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

Je veux trouver l'élément privé _bar que je marquerai avec un attribut. Est-ce possible?

J'ai fait cela avec des propriétés où j'ai cherché un attribut, mais jamais un champ membre privé.

Quels sont les indicateurs de liaison que je dois définir pour obtenir les champs privés?

David Basarab
la source
@Nescio: Pouvez-vous expliquer pourquoi vous adopteriez cette approche? ...les avantages? Ou tout simplement la préférence? :)
IAbstract

Réponses:

279

Utilisation BindingFlags.NonPublicet BindingFlags.Instancedrapeaux

FieldInfo[] fields = myType.GetFields(
                         BindingFlags.NonPublic | 
                         BindingFlags.Instance);
Bob King
la source
11
Je ne pouvais que faire fonctionner cela en fournissant également l'indicateur de liaison "BindingFlags.Instance".
Andy McCluggage
1
J'ai corrigé ta réponse. C'est trop déroutant sinon. La réponse d'Abe Heidebrecht était cependant la plus complète.
lubos hasko
2
Fonctionne très bien - version FYI VB.NET Me.GetType (). GetFields (Reflection.BindingFlags.NonPublic ou Reflection.BindingFlags.Instance)
gg.
2
L'utilisation de l'indicateur de liaison d'instance est uniquement si vous souhaitez obtenir des méthodes d'instance. Si vous vouliez obtenir une méthode statique privée, vous pouvez utiliser (BindingFlags.NonPublic | BindingFlags.Static)
ksun
166

Vous pouvez le faire comme avec une propriété:

FieldInfo fi = typeof(Foo).GetField("_bar", BindingFlags.NonPublic | BindingFlags.Instance);
if (fi.GetCustomAttributes(typeof(SomeAttribute)) != null)
    ...
Abe Heidebrecht
la source
9
Désolé pour la nécro-publication extrême, mais cela m'a bouleversé. GetCustomAttributes (Type) ne retournera pas null si l'attribut n'est pas trouvé, il retourne simplement un tableau vide.
amnésie
42

Obtenez la valeur d'une variable privée à l'aide de Reflection:

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

Définissez la valeur de la variable privée à l'aide de Reflection:

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

Où objectForFooClass est une instance non nulle pour le type de classe Foo.

Suriya
la source
Une réponse similaire décrit une fonction facile à utiliser GetInstanceField(typeof(YourClass), instance, "someString") as string Comment obtenir la valeur d'un champ privé en C #?
Michael Freidgeim
24

Une chose que vous devez savoir lorsque vous réfléchissez sur des membres privés est que si votre application s'exécute en confiance moyenne (comme, par exemple, lorsque vous exécutez sur un environnement d'hébergement partagé), elle ne les trouvera pas - le L'option BindingFlags.NonPublic sera simplement ignorée.

confitures
la source
jammycakes pourriez-vous s'il vous plaît donner un exemple d'environnement d'hébergement partagé? je pense que iis avec plusieurs applications est ce que vous obtenez?
Brian Sweeney
Je parle de l'endroit où IIS est verrouillé pour une confiance partielle au niveau machine.config. Vous ne trouvez généralement cela que sur des plans d'hébergement Web partagé bon marché et désagréables ces jours-ci (ce que je n'utilise plus) - si vous avez un contrôle total sur votre serveur, cela ne sera pas vraiment pertinent, car la confiance totale est la défaut.
jammycakes
18
typeof(MyType).GetField("fieldName", BindingFlags.NonPublic | BindingFlags.Instance)
Darren Kopp
la source
Je ne connais pas le nom du champ. Je veux le trouver sans le nom et quand l'attribut est dessus.
David Basarab
Pour trouver le nom du champ, c'est facile à faire dans Visual Studio. Définissez le point d'arrêt sur la variable, affichez ses champs (y compris le privé, généralement commencé par m_fieldname). Remplacez ce m_fieldname dans la commande ci-dessus.
Hao Nguyen
13

Belle syntaxe avec la méthode d'extension

Vous pouvez accéder à n'importe quel champ privé de type arbitraire avec un code comme celui-ci:

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

Pour cela, vous devez définir une méthode d'extension qui fera le travail pour vous:

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
la source
1
Mec, c'était PARFAIT pour accéder à une variable protégée sans l'exposer à NLua dans mon code! Impressionnant!
tayoung
6

J'utilise cette méthode personnellement

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

Voici quelques méthodes d'extension pour obtenir et définir des champs et propriétés privés simples (propriétés avec setter):

exemple d'utilisation:

    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
la source
4

Oui, mais vous devrez définir vos indicateurs de liaison pour rechercher des champs privés (si vous recherchez le membre en dehors de l'instance de classe).

L'indicateur de liaison dont vous aurez besoin est: System.Reflection.BindingFlags.NonPublic

mmattax
la source
2

Je suis tombé sur cela en recherchant cela sur google, donc je me rends compte que je suis en train de tomber sur un ancien post. Cependant, GetCustomAttributes nécessite deux paramètres.

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

Le deuxième paramètre spécifie si vous souhaitez ou non rechercher la hiérarchie d'héritage

Canonnier
la source