2009-10-14 27 views
104

Tôi có thể đặt thuộc tính riêng tư thông qua phản ánh không?Có thể đặt thuộc tính riêng tư thông qua phản chiếu không?

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ôi đã thử những điều sau đây và nó không hoạt động, nơi t đại diện cho một loại Entity:

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

Tôi đoán tôi có thể làm điều này nhưng tôi không thể làm việc nó ra.

+2

Tôi biết điều này là muộn, nhưng tôi thấy cần thiết cho ý nghĩ này tôi sẽ chia sẻ 'lý do' của mình. Tôi cần khắc phục sự bất tiện trong một số phần mềm của bên thứ ba. Cụ thể, tôi đã sử dụng phương thức Crystal Reports ExportToStream. Cách thức phương pháp này được viết, quyền truy cập vào bộ đệm trong của luồng không được phép. Để gửi báo cáo tới trình duyệt, tôi phải sao chép luồng vào bộ đệm mới (100K +), sau đó gửi nó ra. Bằng cách đặt trường '_exposable' riêng trong đối tượng luồng thành 'true', tôi có thể gửi trực tiếp bộ đệm bên trong, lưu phân bổ 100K + cho mỗi yêu cầu. – Ray

+20

Tại sao? Giả sử bạn có các trình cài đặt riêng trên các thuộc tính Id của bạn trên tất cả đối tượng miền của bạn và bạn muốn triển khai các kiểm tra kho lưu trữ. Sau đó, chỉ trong dự án kiểm tra kho lưu trữ của bạn, bạn sẽ muốn có thể đặt thuộc tính Id. – bounav

+2

Một tình huống sử dụng khác: đặt các trường được tạo tự động như "ngày tạo" khi nhập dữ liệu. – ANeves

Trả lời

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

EDIT: Vì bản thân tài sản là công khai, bạn dường như không cần sử dụng BindingFlags.NonPublic để tìm. Gọi số SetValue mặc dù setter có ít khả năng tiếp cận hơn vẫn làm những gì bạn mong đợi.

+1

Ý của bạn là gì? Tôi đã loại bỏ LINQPad và thử nó và nó đã hoạt động ... – Tinister

+5

Để công bằng, nó phụ thuộc vào mức độ tin tưởng, nhưng câu trả lời có vẻ hợp lệ. –

+2

Phương thức thiết lập thuộc tính không tìm thấy tại System.Reflection.RuntimePropertyInfo.SetValue (Object obj, Object value, BindingFlags invokeAttr, Binder binder, Object [] index, CultureInfo culture) – CZahrobsky

97

Vâng, đó là:

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

Chỉ để an toàn cho ai đó elses tóc (đã được chỉ kéo ra trên đầu của tôi): điều này sẽ không làm việc trong Silverlight runtimes: http://msdn.microsoft.com/de-de/library/xb5dd1f1%28v=vs. 95% 29.aspx –

+0

SetValue sẽ tốt hơn InvokeMember, vì hỗ trợ cũ chỉ số đi qua –

5

Bạn có thể truy cập vào setter tin từ loại có nguồn gốc thông qua mã

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, Nhưng chỉ cần lưu ý ở đây. BaseType nên có tất cả các thuộc tính mà bạn đang mong đợi. Nếu bạn đang ẩn một tài sản (mà không nhớ rằng bạn đã làm như vậy), nó có thể dẫn đến một số tóc được kéo ra ngoài. – 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

Không ai trong số những hoạt động đối với tôi, và tên thuộc tính của tôi là duy nhất, vì vậy tôi chỉ sử dụng điều này:

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; 
     } 
    } 
} 
Các vấn đề liên quan