É possível definir propriedade privada via reflexão?

125

Posso definir uma propriedade privada por reflexão?

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

Eu tentei o seguinte e não funciona, onde trepresenta um tipo de Entity:

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

Acho que posso fazer isso, mas não consigo resolver.

AwkwardCoder
fonte
2
Sei que já é tarde, mas achei necessário compartilhar esse meu 'porquê'. Eu precisava superar um inconveniente em alguns softwares de terceiros. Especificamente, eu estava usando o método Crystal Reports ExportToStream. Da maneira como esse método foi escrito, o acesso ao buffer interno do fluxo não era permitido. Para enviar o relatório para o navegador, tive que copiar o fluxo em um novo buffer (100K +) e enviá-lo. Ao definir o campo privado '_exposable' no objeto de fluxo como 'true', pude enviar diretamente o buffer interno, economizando uma alocação de 100K + em cada solicitação.
Raio
20
Por quê? Digamos que você tenha setters privados nas suas propriedades de ID em todos os objetos do seu domínio e deseje implementar testes de repositório. Somente no seu projeto de teste de repositório você poderá definir a propriedade Id.
bounav
2
Outro cenário de uso: configuração de campos gerados automaticamente como "data de criação" ao importar dados.
ANeves
Outro motivo é que estou curioso para saber se é possível. Foi assim que acabei vendo essa pergunta.
Caleb Mauer 01/02

Respostas:

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

EDIT: Como a propriedade em si é pública, você aparentemente não precisa usá BindingFlags.NonPublic-la para encontrá-la. Ligar, SetValueapesar de o setter ter menos acessibilidade, ainda faz o que você espera.

Tinister
fonte
5
Para ser justo, depende do nível de confiança, mas a resposta parece válida.
Marc Gravell
4
Método conjunto de propriedades não encontradas no System.Reflection.RuntimePropertyInfo.SetValue (Object obj, valor objecto, invokeAttr BindingFlags, Binder ligante objecto índice [], a cultura CultureInfo)
CZahrobsky
1
Isso funciona bem para mim se eu não estiver usando uma propriedade virtual. Se eu definir valor com uma propriedade virtual, isso não parece funcionar.
JonathanPeel
105

Sim, ele é:

/// <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
fonte
7
Apenas para proteger alguém do cabelo (que acabou de ser arrancado da minha cabeça): isso não funcionará nos tempos de execução do Silverlight: msdn.microsoft.com/de-de/library/xb5dd1f1%28v=vs.95%29.aspx
Marc Wittke
SetValue seria melhor do que InvokeMember, desde os antigos suportes que passam índice
Chris Xue
8

Você pode acessar o setter privado do tipo derivado via código

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
fonte
+1, mas apenas uma nota aqui. BaseType deve ter todas as propriedades que você espera. Se você estiver ocultando uma propriedade (sem se lembrar de ter feito isso), isso poderá resultar em arrancamento de alguns cabelos.
precisa saber é
3

Nada disso funcionou para mim, e o nome da minha propriedade era único, então eu apenas usei isso:

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