Apakah mungkin menetapkan properti pribadi melalui refleksi?

125

Dapatkah saya menetapkan properti pribadi melalui refleksi?

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

Saya sudah mencoba yang berikut ini dan tidak berhasil, di mana tmewakili jenis dari Entity:

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

Saya kira saya bisa melakukan ini tetapi saya tidak bisa menyelesaikannya.

AwkwardCoder
sumber
2
Saya tahu ini terlambat, tetapi saya menemukan kebutuhan untuk pemikiran ini, saya akan membagikan 'mengapa' saya. Saya perlu mengatasi ketidaknyamanan di beberapa perangkat lunak pihak ketiga. Secara khusus, saya menggunakan metode Crystal Reports ExportToStream. Cara metode ini ditulis, akses ke buffer internal aliran tidak diizinkan. Untuk mengirim laporan ke browser, saya harus menyalin aliran ke buffer baru (100K +), lalu mengirimkannya. Dengan menyetel bidang pribadi '_exposable' di objek aliran ke 'benar', saya dapat mengirim buffer internal keluar secara langsung, menghemat alokasi 100K + pada setiap permintaan.
Ray
20
Mengapa? Katakanlah Anda memiliki penyetel pribadi pada properti Id Anda di semua objek domain Anda dan Anda ingin mengimplementasikan pengujian repositori. Kemudian, hanya dalam proyek pengujian repositori Anda, Anda ingin dapat menyetel properti Id.
bounav
2
Skenario penggunaan lainnya: menyetel bidang yang dibuat secara otomatis seperti "tanggal pembuatan" saat mengimpor data.
ANeves
Alasan lainnya adalah saya hanya ingin tahu apakah itu mungkin. Begitulah akhirnya saya melihat pertanyaan ini.
Caleb Mauer

Jawaban:

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

EDIT: Karena properti itu sendiri bersifat publik, Anda tampaknya tidak perlu menggunakan BindingFlags.NonPublicuntuk menemukannya. Memanggil SetValuemeskipun penyetel memiliki aksesibilitas yang lebih rendah tetap melakukan apa yang Anda harapkan.

Tinister
sumber
5
Agar adil, itu tergantung pada tingkat kepercayaan, tetapi jawabannya tampaknya valid.
Marc Gravell
4
Metode set properti tidak ditemukan di System.Reflection.RuntimePropertyInfo.SetValue (Object obj, nilai Object, BindingFlags invokeAttr, Binder binder, Object [] index, CultureInfo culture)
CZahrobsky
1
Ini berfungsi dengan baik untuk saya jika saya tidak menggunakan properti virtual. Jika saya SetValue dengan properti virtual, ini tampaknya tidak berfungsi.
JonathanPeel
105

Ya itu:

/// <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);
}
Arthur
sumber
7
Hanya untuk mengamankan rambut orang lain (yang baru saja ditarik keluar di kepala saya): ini tidak akan berfungsi di runtime Silverlight: msdn.microsoft.com/de-de/library/xb5dd1f1%28v=vs.95%29.aspx
Marc Wittke
SetValue akan lebih baik daripada InvokeMember, karena yang pertama mendukung indeks kelulusan
Chris Xue
8

Anda dapat mengakses penyetel pribadi dari tipe turunan melalui kode

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);
}
Siarhei Kuchuk
sumber
+1, Tapi hanya catatan di sini. BaseType harus memiliki semua properti yang Anda harapkan. Jika Anda menyembunyikan properti (tanpa ingat Anda pernah melakukannya), hal itu bisa menyebabkan beberapa rambut tercabut.
ouflak
3

Tidak ada yang berhasil untuk saya, dan nama properti saya unik, jadi saya hanya menggunakan ini:

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;
        }
    }
}
CZahrobsky
sumber
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
BTE
sumber