Có thể thiết lập tài sản tư nhân thông qua sự phản ánh?


125

Tôi có thể thiết lập một tài sản riêng thông qua sự phản ánh?

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ử các cách sau và nó không hoạt động, trong đó 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 được.


2
Tôi biết điều này là muộn, nhưng tôi thấy cần một suy nghĩ này, tôi sẽ chia sẻ 'tại sao' 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 đang sử dụng phương thức Crystal báo cáo xuất khẩu. Cách viết phương pháp này, không được phép truy cập vào bộ đệm bên trong của luồng. Để 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 '_ex Dùng riêng' trong đối tượng luồng thành 'true', tôi có thể gửi bộ đệm nội bộ trực tiếp ra ngoài, tiết kiệm phân bổ 100K + cho mỗi yêu cầu.
Ray

20
Tại sao? Giả sử bạn có setters riêng trên các thuộc tính Id trên tất cả các đối tượng miền của bạn và bạn muốn triển khai 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 mới muốn có thể đặt thuộc tính Id.
bounav

2
Một kịch bản sử dụng khác: thiết lập các trường được tạo tự động như "ngày tạo" khi nhập dữ liệu.
ANeves

Một lý do khác tại sao tôi chỉ tò mò nếu nó có thể. Đó là cách tôi đã xem câu hỏi này.
Caleb Mauer

Câu trả lời:


94
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, nên rõ ràng bạn không cần sử dụng BindingFlags.NonPublicđể tìm nó. Gọi SetValuemặc dù trình cài đặt có ít khả năng truy cập hơn vẫn làm những gì bạn mong đợi.


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

4
Không tìm thấy phương thức thiết lập thuộc tính tại System.Reflection.R nbPropertyInfo.SetValue (Object obj, Object value, BindingFlags invokeAttr, Binder binder, Object [] index, CultureInfo Culture)
CZahrobsky 24/8/2016

1
Điều này hoạt động tốt với tôi nếu tôi không sử dụng một tài sản ảo. Nếu tôi SetValue có thuộc tính ảo, điều này dường như không hoạt động.
JonathanPeel

105

Vâng, đúng vậy:

/// <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 tóc của ai đó (vừa mới nhổ trên đầu tôi): điều này sẽ không hoạt động trong thời gian chạy của Silverlight: msdn.microsoft.com/de-de/l Library / xb5dd1f1% 28v = vs.95% 29.aspx
Marc Wittke

SetValue sẽ tốt hơn InvokeMember, vì cựu chỉ số hỗ trợ vượt qua
Chris Xue

8

Bạn có thể truy cập setter riêng từ loại dẫn xuất 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);
}

+1, nhưng chỉ là một 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 giấu 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 bị kéo ra.
ngerak

3

Không ai trong số này làm việc cho tôi và tên tài sản của tôi là duy nhất, vì vậy tôi chỉ sử dụng cái 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;
        }
    }
}

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.