2009-10-14 4 views
104

Puis-je définir une propriété privée par réflexion?Est-il possible de définir la propriété privée par réflexion?

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); } 
    } 
} 

J'ai essayé le fait de suivre et il fonctionne pas, où t représente un type de Entity:

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

Je pense que je peux le faire, mais je ne peux pas travailler dehors.

+2

Je le sais est en retard, mais j'ai trouvé un besoin pour cette pensée que je partagerais mon «pourquoi». J'avais besoin de surmonter un inconvénient dans certains logiciels tiers. Plus précisément, j'utilisais la méthode Crystal Reports ExportToStream. La façon dont cette méthode a été écrite, l'accès au tampon interne du flux n'a pas été autorisé. Afin d'envoyer le rapport au navigateur, j'ai dû copier le flux dans un nouveau tampon (100K +), puis l'envoyer. En définissant le champ privé '_exposable' dans l'objet stream sur 'true', j'ai pu envoyer le buffer interne directement, en sauvegardant une allocation de 100K + sur chaque requête. – Ray

+20

Pourquoi? Disons que vous avez des setters privés sur vos propriétés Id sur tous les objets de votre domaine et que vous souhaitez implémenter des tests de référentiel. Ensuite, uniquement dans votre projet de test de référentiel, vous souhaiterez pouvoir définir la propriété Id. – bounav

+2

Un autre scénario d'utilisation: définir des champs générés automatiquement comme "date de création" lors de l'importation de données. – ANeves

Répondre

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

EDIT: Étant donné que la propriété elle-même est publique, vous n'avez pas besoin apparemment d'utiliser BindingFlags.NonPublic pour le trouver. Appeler SetValue malgré le fait que le setter ayant moins d'accessibilité fait toujours ce que vous attendez.

+1

Que voulez-vous dire? J'ai fouetté LINQPad et essayé cela et cela a fonctionné ... – Tinister

+5

Pour être juste, cela dépend du niveau de confiance, mais la réponse semble valable. –

+2

Méthode de jeu de propriétés introuvable à System.Reflection.RuntimePropertyInfo.SetValue (Object obj, Object value, BindingFlags invokeAttr, Classeur de classeur, Object [] index, CultureInfo culture) – CZahrobsky

97

Oui, il est:

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

Juste pour mettre en sécurité les cheveux des autres (qui viennent d'être arrachés sur ma tête): cela ne fonctionnera pas dans les environnements d'exécution de Silverlight: http://msdn.microsoft.com/de-de/library/xb5dd1f1%28v=vs. 95% 29.aspx –

+0

SetValue serait mieux que InvokeMember, puisque le premier supporte l'indice de passage –

5

Vous pouvez accéder setter privé de type dérivé via le code

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, mais juste une note ici. BaseType devrait avoir toutes les propriétés que vous attendez. Si vous cachez une propriété (sans vous souvenir que vous l'avez fait), il se peut que certains cheveux soient arrachés. – 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

Aucune de ces travaillé pour moi, et mon nom de la propriété était unique, donc je l'ai juste utilisé:

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; 
     } 
    } 
} 
Questions connexes