เป็นไปได้ไหมที่จะตั้งค่าคุณสมบัติส่วนตัวผ่านการสะท้อน?


125

ฉันสามารถตั้งค่าคุณสมบัติส่วนตัวผ่านการสะท้อนแสงได้หรือไม่?

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" ส่วนตัวในวัตถุสตรีมเป็น "จริง" ฉันสามารถส่งบัฟเฟอร์ภายในออกได้โดยตรงโดยประหยัดการจัดสรร 100K + สำหรับแต่ละคำขอ
Ray

20
ทำไม? สมมติว่าคุณมีตัวตั้งค่าส่วนตัวในคุณสมบัติ Id ของคุณในออบเจ็กต์โดเมนทั้งหมดของคุณและคุณต้องการใช้การทดสอบที่เก็บ จากนั้นในโครงการทดสอบที่เก็บของคุณเท่านั้นที่คุณจะต้องสามารถตั้งค่าคุณสมบัติ Id ได้
bounav

2
สถานการณ์การใช้งานอื่น: การตั้งค่าช่องที่สร้างขึ้นโดยอัตโนมัติเช่น "วันที่สร้าง" เมื่อนำเข้าข้อมูล
ANeves

อีกอย่างทำไมฉันแค่อยากรู้ว่าเป็นไปได้ไหม นั่นคือวิธีที่ฉันดูคำถามนี้
Caleb Mauer

คำตอบ:


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

แก้ไข: เนื่องจากทรัพย์สินนั้นเป็นของสาธารณะคุณจึงไม่จำเป็นต้องใช้BindingFlags.NonPublicเพื่อค้นหา การโทรSetValueแม้ว่าผู้ตั้งค่าจะมีความสามารถในการเข้าถึงน้อยกว่าก็ยังทำในสิ่งที่คุณคาดหวังได้


5
เพื่อความเป็นธรรมขึ้นอยู่กับระดับความไว้วางใจ แต่คำตอบดูเหมือนจะใช้ได้
Marc Gravell

4
ไม่พบวิธีการตั้งค่าคุณสมบัติที่ System.Reflection.RuntimePropertyInfo.SetValue (Object obj, Object value, BindingFlags invokeAttr, Binder binder, Object [] index, CultureInfo culture)
CZahrobsky

1
วิธีนี้ใช้ได้ดีสำหรับฉันถ้าฉันไม่ได้ใช้คุณสมบัติเสมือน ถ้าฉัน SetValue ด้วยคุณสมบัติเสมือนดูเหมือนจะไม่ทำงาน
JonathanPeel

105

ใช่แล้ว:

/// <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 runtimes: msdn.microsoft.com/de-de/library/xb5dd1f1%28v=vs.95%29.aspx
Marc Wittke

SetValue จะดีกว่า InvokeMember เนื่องจากเดิมรองรับดัชนีการส่งผ่าน
Chris Xue

8

คุณสามารถเข้าถึงตัวตั้งค่าส่วนตัวจากประเภทที่ได้รับผ่านรหัส

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 แต่โปรดทราบไว้ที่นี่ BaseType ควรมีคุณสมบัติทั้งหมดที่คุณคาดหวัง หากคุณซ่อนทรัพย์สิน (โดยที่จำไม่ได้ว่าทำไปแล้ว) อาจส่งผลให้ผมบางส่วนถูกดึงออก
camouflageak

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

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.