2009-10-14 16 views
104

¿Puedo establecer una propiedad privada a través de la reflexión?¿Es posible establecer propiedad privada a través de la reflexión?

public abstract class Entity 
{ 
    private int _id; 
    private DateTime? _createdOn; 
    public virtual T Id 
    { 
     get { return _id; } 
     private set { ChangePropertyAndNotify(ref _id, value, x => Id); } 
    } 
    public virtual DateTime? CreatedOn 
    { 
     get { return _createdOn; } 
     private set { ChangePropertyAndNotify(ref _createdOn, value, x => CreatedOn); } 
    } 
} 

He intentado lo siguiente y no funciona, donde t representa un tipo de Entity:

var t = typeof(Entity); 
var mi = t.GetMethod("set_CreatedOn", BindingFlags.Instance | BindingFlags.NonPublic); 

creo que puedo hacer esto, pero no puedo resolverlo.

+2

Sé esto es tarde, pero encontré la necesidad de este pensamiento, compartiría mi 'por qué'. Necesitaba superar un inconveniente en algún software de terceros. Específicamente, estaba usando el método Crystal Reports ExportToStream. La forma en que se escribió este método no permitía el acceso al búfer interno de la secuencia. Para enviar el informe al navegador, tuve que copiar la secuencia en un nuevo buffer (100K +), luego enviarlo. Al establecer el campo privado '_exponible' en el objeto de transmisión en 'verdadero', pude enviar el búfer interno directamente, guardando una asignación de 100K + en cada solicitud. – Ray

+20

¿Por qué? Supongamos que tiene instaladores privados en sus propiedades de Id en todo su objeto de dominio y desea implementar pruebas de repositorio. Entonces, solo en su proyecto de prueba de repositorio, querrá poder establecer la propiedad Id. – bounav

+2

Otro escenario de uso: configuración de campos generados automáticamente como "fecha de creación" al importar datos. – ANeves

Respuesta

67
t.GetProperty("CreatedOn") 
    .SetValue(obj, new DateTime(2009, 10, 14), null); 

EDIT: Ya que la propiedad en sí es público, que al parecer no es necesario utilizar BindingFlags.NonPublic para encontrarlo. Llamar al SetValue a pesar de que el colocador tenga menos accesibilidad sigue haciendo lo que espera.

+1

¿Qué quieres decir? Saqué LINQPad e intenté esto y funcionó ... – Tinister

+5

Para ser justos, depende del nivel de confianza, pero la respuesta parece válida. –

+2

Método de conjunto de propiedades no encontrado en System.Reflection.RuntimePropertyInfo.SetValue (Object obj, Object value, BindingFlags invokeAttr, Binder Binder, Object [] index, CultureInfo culture) – CZahrobsky

97

sí, es:

/// <summary> 
    /// Returns a _private_ Property Value from a given Object. Uses Reflection. 
    /// Throws a ArgumentOutOfRangeException if the Property is not found. 
    /// </summary> 
    /// <typeparam name="T">Type of the Property</typeparam> 
    /// <param name="obj">Object from where the Property Value is returned</param> 
    /// <param name="propName">Propertyname as string.</param> 
    /// <returns>PropertyValue</returns> 
    public static T GetPrivatePropertyValue<T>(this object obj, string propName) 
    { 
     if (obj == null) throw new ArgumentNullException("obj"); 
     PropertyInfo pi = obj.GetType().GetProperty(propName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); 
     if (pi == null) throw new ArgumentOutOfRangeException("propName", string.Format("Property {0} was not found in Type {1}", propName, obj.GetType().FullName)); 
     return (T)pi.GetValue(obj, null); 
    } 

    /// <summary> 
    /// Returns a private Property Value from a given Object. Uses Reflection. 
    /// Throws a ArgumentOutOfRangeException if the Property is not found. 
    /// </summary> 
    /// <typeparam name="T">Type of the Property</typeparam> 
    /// <param name="obj">Object from where the Property Value is returned</param> 
    /// <param name="propName">Propertyname as string.</param> 
    /// <returns>PropertyValue</returns> 
    public static T GetPrivateFieldValue<T>(this object obj, string propName) 
    { 
     if (obj == null) throw new ArgumentNullException("obj"); 
     Type t = obj.GetType(); 
     FieldInfo fi = null; 
     while (fi == null && t != null) 
     { 
      fi = t.GetField(propName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); 
      t = t.BaseType; 
     } 
     if (fi == null) throw new ArgumentOutOfRangeException("propName", string.Format("Field {0} was not found in Type {1}", propName, obj.GetType().FullName)); 
     return (T)fi.GetValue(obj); 
    } 

    /// <summary> 
    /// Sets a _private_ Property Value from a given Object. Uses Reflection. 
    /// Throws a ArgumentOutOfRangeException if the Property is not found. 
    /// </summary> 
    /// <typeparam name="T">Type of the Property</typeparam> 
    /// <param name="obj">Object from where the Property Value is set</param> 
    /// <param name="propName">Propertyname as string.</param> 
    /// <param name="val">Value to set.</param> 
    /// <returns>PropertyValue</returns> 
    public static void SetPrivatePropertyValue<T>(this object obj, string propName, T val) 
    { 
     Type t = obj.GetType(); 
     if (t.GetProperty(propName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) == null) 
      throw new ArgumentOutOfRangeException("propName", string.Format("Property {0} was not found in Type {1}", propName, obj.GetType().FullName)); 
     t.InvokeMember(propName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetProperty | BindingFlags.Instance, null, obj, new object[] { val }); 
    } 

    /// <summary> 
    /// Set a private Property Value on a given Object. Uses Reflection. 
    /// </summary> 
    /// <typeparam name="T">Type of the Property</typeparam> 
    /// <param name="obj">Object from where the Property Value is returned</param> 
    /// <param name="propName">Propertyname as string.</param> 
    /// <param name="val">the value to set</param> 
    /// <exception cref="ArgumentOutOfRangeException">if the Property is not found</exception> 
    public static void SetPrivateFieldValue<T>(this object obj, string propName, T val) 
    { 
     if (obj == null) throw new ArgumentNullException("obj"); 
     Type t = obj.GetType(); 
     FieldInfo fi = null; 
     while (fi == null && t != null) 
     { 
      fi = t.GetField(propName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); 
      t = t.BaseType; 
     } 
     if (fi == null) throw new ArgumentOutOfRangeException("propName", string.Format("Field {0} was not found in Type {1}", propName, obj.GetType().FullName)); 
     fi.SetValue(obj, val); 
    } 
+7

Solo para proteger el pelo de otra persona (que acaba de ser sacado de mi cabeza): esto no funcionará en los tiempos de ejecución de Silverlight: http://msdn.microsoft.com/de-de/library/xb5dd1f1%28v=vs. 95% 29.aspx –

+0

SetValue sería mejor que InvokeMember, ya que el primero soporta pasar el índice –

5

Puede acceder incubadora privada de tipo derivado a través de código

public static void SetProperty(object instance, string propertyName, object newValue) 
{ 
    Type type = instance.GetType(); 

    PropertyInfo prop = type.BaseType.GetProperty(propertyName); 

    prop.SetValue(instance, newValue, null); 
} 
+0

+1, pero solo una nota aquí. BaseType debe tener todas las propiedades que está esperando. Si está ocultando una propiedad (sin recordar que lo había hecho), podría sacar algo de cabello. – ouflak

0
//mock class 
    public class Person{ 
     public string Name{get; internal set;} 
    } 

    // works for all types, update private field through reflection 
    public static T ReviveType<T>(T t, string propertyName, object newValue){ 
     // add a check here that the object t and propertyName string are not null 
     PropertyInfo pi = t.GetType().GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance); 
     pi.SetValue(t, newValue, null); 
     return t; 
    } 

    // check the required function 
    void Main() 
    { 
     var p = new Person(){Name="John"}; 
     Console.WriteLine("Name: {0}",p.Name); 

     //box the person to object, just to see that the method never care about what type you pass it 
     object o = p; 
     var updatedPerson = ReviveType<Object>(o, "Name", "Webber") as Person; 

     //check if it updated person instance 
     Console.WriteLine("Name: {0}",updatedPerson.Name); 
    } 



// Console Result: ------------------- 
Name: John 
Name: Webber 
3

Ninguno de ellos trabajó para mí, y mi nombre de la propiedad era único, así que acabo de utilizar esto:

public static void SetPrivatePropertyValue<T>(T obj, string propertyName, object newValue) 
{ 
    // add a check here that the object obj and propertyName string are not null 
    foreach (FieldInfo fi in obj.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic)) 
    { 
     if (fi.Name.ToLower().Contains(propertyName.ToLower())) 
     { 
      fi.SetValue(obj, newValue); 
      break; 
     } 
    } 
} 
Cuestiones relacionadas