2009-10-14 136 views
104

我可以通過反射設置私人房產嗎?是否可以通過反射來設置私有財產?

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

我試過以下,它不工作,其中t代表一種類型的Entity

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

我想我能做到這一點,但我不能工作了。

+2

我知道這個已經很晚了,但我發現需要這個想法,我會分享我的'爲什麼'。我需要克服一些第三方軟件帶來​​的不便。具體來說,我使用了Crystal Reports的ExportToStream方法。這種方法寫入的方式,不允許訪問流的內部緩衝區。爲了將報告發送到瀏覽器,我必須將該流複製到新的緩衝區(100K +)中,然後將其發送出去。通過將流對象中的私有'_exposable'字段設置爲'true',我可以直接發送內部緩衝區,爲每個請求節省100K +的分配。 – Ray 2009-12-06 23:47:32

+20

爲什麼?假設您的所有域對象上的Id屬性都有私人設置器,並且您想要實施存儲庫測試。然後,只有在您的存儲庫測試項目中,您才能夠設置Id屬性。 – bounav 2010-06-17 15:44:39

+2

另一種使用場景:導入數據時設置自動生成的字段,如「創建日期」。 – ANeves 2014-11-12 18:50:28

回答

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

編輯:由於物業本身是公開的,你顯然不需要使用BindingFlags.NonPublic找到它。儘管設置器具有較低的可訪問性,但調用SetValue仍然符合您的期望。

+1

你是什麼意思?我鞭打LINQPad,並試圖這個,它的工作... – Tinister 2009-10-14 11:55:55

+5

公平,它取決於信任水平,但答案似乎有效。 – 2009-12-12 12:27:33

+2

在System.Reflection.RuntimePropertyInfo.SetValue找不到屬性設置方法(Object obj,Object value,BindingFlags invokeAttr,Binder binder,Object [] index,CultureInfo culture) – CZahrobsky 2016-08-24 20:08:40

97

是的,這就是:

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

僅僅爲了安全起見,別人的頭髮(剛剛被拔出):這不適用於Silverlight運行時:http://msdn.microsoft.com/de-de/library/xb5dd1f1%28v=vs。 95%29.aspx – 2012-04-20 10:04:23

+0

SetValue會比InvokeMember好,因爲前者支持傳遞索引 – 2016-06-14 13:30:43

5

您可以從派生類型通過代碼

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,但這裏只是一個註釋。 BaseType應該具有您所期望的所有屬性。如果你隱藏了一個財產(不記得你已經這樣做了),它可能會導致一些頭髮被拉出。 – ouflak 2016-09-23 06:24:40

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

這些都不工作對我來說,我的屬性名稱是獨一無二的訪問私有二傳手,所以我只是用這個:

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