È possibile impostare la proprietà privata tramite la riflessione?


125

Posso impostare una proprietà privata tramite la riflessione?

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

Ho provato quanto segue e non funziona, dove trappresenta un tipo di Entity:

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

Immagino di poterlo fare ma non riesco a risolverlo.


2
So che è tardi, ma ho trovato la necessità di questo pensiero che condividerei il mio "perché". Ho dovuto superare un inconveniente in alcuni software di terze parti. In particolare, stavo usando il metodo ExportToStream di Crystal Reports. Nel modo in cui questo metodo è stato scritto, l'accesso al buffer interno del flusso non è stato consentito. Per inviare il rapporto al browser, ho dovuto copiare il flusso in un nuovo buffer (100K +), quindi inviarlo. Impostando il campo privato "_exposable" nell'oggetto stream su "true", sono stato in grado di inviare direttamente il buffer interno, salvando un'allocazione di 100K + su ogni richiesta.
Ray,

20
Perché? Supponiamo che tu abbia setter privati ​​sulle proprietà del tuo ID su tutti i tuoi oggetti di dominio e desideri implementare test di repository. Quindi solo nel progetto di test del repository ti consigliamo di impostare la proprietà Id.
Bounav,

2
Un altro scenario d'uso: impostazione di campi generati automaticamente come "data di creazione" durante l'importazione dei dati.
ANeves,

Un altro motivo è che sono solo curioso di sapere se è possibile. È così che ho finito per vedere questa domanda.
Caleb Mauer,

Risposte:


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

EDIT: Dal momento che la proprietà stessa è pubblica, apparentemente non è necessario utilizzare BindingFlags.NonPublicper trovarla. Chiamare SetValuenonostante il setter abbia meno accessibilità fa ancora quello che ti aspetti.


5
Ad essere onesti, dipende dal livello di fiducia, ma la risposta sembra valida.
Marc Gravell

4
Metodo dell'insieme di proprietà non trovato in System.Reflection.RuntimePropertyInfo.SetValue (Object obj, Object value, BindingFlags invokeAttr, Binder binder, Object [] index, CultureInfo culture)
CZahrobsky

1
Questo funziona bene per me se non sto usando una proprietà virtuale. Se SetValue con una proprietà virtuale, questo non sembra funzionare.
Jonathan

105

Sì:

/// <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
Solo per proteggere i capelli di qualcun altro (che sono stati appena tirati fuori dalla mia testa): questo non funzionerà nei runtime di Silverlight: msdn.microsoft.com/de-de/library/xb5dd1f1%28v=vs.95%29.aspx
Marc Wittke,

SetValue sarebbe meglio di InvokeMember, poiché il primo supporta l'indice di passaggio
Chris Xue,

8

È possibile accedere al setter privato dal tipo derivato tramite codice

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, ma solo una nota qui. BaseType dovrebbe avere tutte le proprietà che ti aspetti. Se stai nascondendo una proprietà (senza ricordare che l'hai fatto), potrebbe comportare la rimozione di alcuni capelli.
ouflak,

3

Nessuno di questi ha funzionato per me e il mio nome di proprietà era univoco, quindi ho usato solo questo:

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.