¿Encontrar un campo privado con Reflection?

228

Dada esta clase

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

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

Quiero encontrar el elemento privado _bar que marcaré con un atributo. ¿Es eso posible?

He hecho esto con propiedades donde he buscado un atributo, pero nunca un campo de miembro privado.

¿Cuáles son los indicadores de enlace que debo establecer para obtener los campos privados?

David Basarab
fuente
@Nescio: ¿Puedes ampliar por qué tomarías ese enfoque? ...¿los beneficios? O simplemente preferencia? :)
IAbstract

Respuestas:

279

Uso BindingFlags.NonPublicy BindingFlags.Instancebanderas

FieldInfo[] fields = myType.GetFields(
                         BindingFlags.NonPublic | 
                         BindingFlags.Instance);
Bob King
fuente
11
Solo pude hacer que esto funcione al proporcionar también el indicador de enlace "BindingFlags.Instance".
Andy McCluggage
1
He arreglado tu respuesta. Es muy confuso de lo contrario. Sin embargo, la respuesta de Abe Heidebrecht fue la más completa.
lubos hasko 05 de
2
Funciona muy bien - FYI VB.NET versión Me.GetType (). GetFields (Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance)
gg.
2
El uso del indicador de enlace de instancia es solo si desea obtener métodos de instancia. Si desea obtener un método estático privado, puede usar (BindingFlags.NonPublic | BindingFlags.Static)
ksun
166

Puedes hacerlo como con una propiedad:

FieldInfo fi = typeof(Foo).GetField("_bar", BindingFlags.NonPublic | BindingFlags.Instance);
if (fi.GetCustomAttributes(typeof(SomeAttribute)) != null)
    ...
Abe Heidebrecht
fuente
99
Perdón por la publicación extrema de necro, pero esto me desanimó. GetCustomAttributes (Type) no devolverá nulo si no se encuentra el atributo, simplemente devuelve una matriz vacía.
amnesia
42

Obtenga el valor de la variable privada usando Reflection:

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

Establezca el valor para la variable privada usando Reflection:

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

Donde objectForFooClass es una instancia no nula para el tipo de clase Foo.

Suriya
fuente
Una respuesta similar describe una función fácil de usar GetInstanceField(typeof(YourClass), instance, "someString") as string ¿Cómo obtener el valor del campo privado en C #?
Michael Freidgeim
24

Una cosa que debe tener en cuenta al reflexionar sobre los miembros privados es que si su aplicación se ejecuta con una confianza media (como, por ejemplo, cuando se ejecuta en un entorno de alojamiento compartido), no los encontrará: el La opción BindingFlags.NonPublic simplemente se ignorará.

jammycakes
fuente
jammycakes, ¿podría dar un ejemplo de entorno de alojamiento compartido? Estoy pensando que iis con múltiples aplicaciones es lo que estás obteniendo?
Brian Sweeney
Estoy hablando de dónde IIS está bloqueado a una confianza parcial en el nivel machine.config. Por lo general, solo encuentra esto en planes de alojamiento web compartidos baratos y desagradables en estos días (como los que ya no uso): si tiene control total sobre su servidor, entonces realmente no será relevante ya que la confianza total es la defecto.
jammycakes
18
typeof(MyType).GetField("fieldName", BindingFlags.NonPublic | BindingFlags.Instance)
Darren Kopp
fuente
No sabré el nombre del campo. Quiero encontrarlo sin el nombre y cuando el atributo está en él.
David Basarab
Para encontrar el nombre del campo, es fácil hacerlo en Visual Studio. Establezca el punto de interrupción en la variable, vea sus campos (incluido el privado, que generalmente comienza con m_fieldname). Reemplace ese m_fieldname en el comando anterior.
Hao Nguyen
13

Buena sintaxis con método de extensión

Puede acceder a cualquier campo privado de tipo arbitrario con un código como este:

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

Para eso, debe definir un método de extensión que haga el trabajo por usted:

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
fuente
1
Amigo, ¡esto era PERFECTO para acceder a una variable protegida sin exponerla a NLua en mi código! ¡Increíble!
tayoung 01 de
6

Yo uso este método personalmente

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

Aquí hay algunos métodos de extensión para obtener y establecer campos privados y propiedades (propiedades con setter):

ejemplo de uso:

    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

Código:

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

Sí, sin embargo, deberá establecer sus indicadores de enlace para buscar campos privados (si está buscando al miembro fuera de la instancia de clase)

El indicador de enlace que necesitará es: System.Reflection.BindingFlags.NonPublic

mmattax
fuente
2

Me encontré con esto mientras buscaba esto en Google, así que me doy cuenta de que estoy tropezando con una publicación anterior. Sin embargo, GetCustomAttributes requiere dos parámetros.

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

El segundo parámetro especifica si desea o no buscar en la jerarquía de herencia

Artillero
fuente