Establecer la propiedad del objeto usando la reflexión

323

¿Hay alguna manera en C # donde pueda usar la reflexión para establecer una propiedad de objeto?

Ex:

MyObject obj = new MyObject();
obj.Name = "Value";

Quiero establecer obj.Namecon reflexión. Algo como:

Reflection.SetProperty(obj, "Name") = "Value";

¿Hay alguna forma de hacer esto?

Melursus
fuente

Respuestas:

391

Sí, puedes usar Type.InvokeMember():

using System.Reflection;
MyObject obj = new MyObject();
obj.GetType().InvokeMember("Name",
    BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty,
    Type.DefaultBinder, obj, "Value");

Esto generará una excepción si objno se llama a una propiedad Nameo si no se puede establecer.

Otro enfoque es obtener los metadatos de la propiedad y luego configurarlo. Esto le permitirá verificar la existencia de la propiedad y verificar que se puede configurar:

using System.Reflection;
MyObject obj = new MyObject();
PropertyInfo prop = obj.GetType().GetProperty("Name", BindingFlags.Public | BindingFlags.Instance);
if(null != prop && prop.CanWrite)
{
    prop.SetValue(obj, "Value", null);
}
Andy
fuente
73
Si no está tratando con todas las cadenas, es posible que desee convertir los datos primero: var val = Convert.ChangeType(propValue, propInfo.PropertyType); fuente: devx.com/vb2themax/Tip/19599
LostNomad311
44
alternativamente, puede usarobj.GetType().GetProperty("Name")?.GetSetMethod()?.Invoke(...)
tecfield
1
Es imposible establecer el valor para los CanWrite=Falsetipos, ¿verdad?
T.Todua
287

También puedes hacer:

Type type = target.GetType();

PropertyInfo prop = type.GetProperty("propertyName");

prop.SetValue (target, propertyValue, null);

donde target es el objeto que tendrá su propiedad establecida.

El Cheicon
fuente
11
He hecho lo mismo hoy. Lo anterior funciona muy bien, obviamente, se debe hacer una comprobación nula en el accesorio antes de intentar usarlo.
Antony Scott
3
@AntonyScott Creo que le gustaría saber si está invocando la propiedad incorrecta, por lo que "fallar en silencio" parece un mal curso.
jih
3
@jih Puedo ver tu punto, pero realmente depende de la situación.
Antony Scott
94

Reflexión, básicamente, es decir

myObject.GetType().GetProperty(property).SetValue(myObject, "Bob", null);

o hay bibliotecas para ayudar tanto en términos de conveniencia como de rendimiento; por ejemplo con FastMember :

var wrapped = ObjectAccessor.Create(obj); 
wrapped[property] = "Bob";

(que también tiene la ventaja de no necesitar saber de antemano si es un campo o una propiedad)

Marc Gravell
fuente
Wow, me confundí un poco por la fusión, ¡pero encontré tu respuesta nuevamente! Gracias, te mereces un 'aceptar', pero como mi hilo se fusionó :( ¡Gracias de nuevo!
halfpastfour.am
@MarcGravell, estaba mirando FastMember y es bastante interesante. ¿Hay algún tutorial de iniciación en algún lugar para nosotros, simples mortales, para usar esta gran biblioteca tuya?
Sudhanshu Mishra
¿Cómo puedo obtener el tipo de propiedad por FastMember?
dijo Roohullah Allem el
@Jahan accessor => GetMembers => Member => Type
Marc Gravell
¡gran respuesta! Lo bueno de un oneliner es que es mucho más rápido de entender, ya que no hay variables nombradas por el usuario entre las cuales no tenga ningún sentido ..
Bastiaan
27

O podría envolver el único revestimiento de Marc dentro de su propia clase de extensión:

public static class PropertyExtension{       

   public static void SetPropertyValue(this object obj, string propName, object value)
    {
        obj.GetType().GetProperty(propName).SetValue(obj, value, null);
    }
}

y llámalo así:

myObject.SetPropertyValue("myProperty", "myValue");

Como buena medida, agreguemos un método para obtener un valor de propiedad:

public static object GetPropertyValue(this object obj, string propName)
{
        return obj.GetType().GetProperty(propName).GetValue (obj, null);
}
Erik K.
fuente
14

Sí, usando System.Reflection:

using System.Reflection;

...

    string prop = "name";
    PropertyInfo pi = myObject.GetType().GetProperty(prop);
    pi.SetValue(myObject, "Bob", null);
D Stanley
fuente
13

Use algo como esto:

public static class PropertyExtension{       

   public static void SetPropertyValue(this object p_object, string p_propertyName, object value)
   {
    PropertyInfo property = p_object.GetType().GetProperty(p_propertyName);
    property.SetValue(p_object, Convert.ChangeType(value, property.PropertyType), null);
   }
}

o

public static class PropertyExtension{       

   public static void SetPropertyValue(this object p_object, string p_propertyName, object value)
   {
    PropertyInfo property = p_object.GetType().GetProperty(p_propertyName);
    Type t = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
    object safeValue = (value == null) ? null : Convert.ChangeType(value, t);

    property.SetValue(p_object, safeValue, null);
   }
}
Ardalan Shahgholi
fuente
2
La parte donde obtienes el tipo de propiedad y luego la lanzas fue realmente útil para mí. Funciona a las mil maravillas. Gracias
Marc
12

También puede acceder a los campos de manera similar:

var obj=new MyObject();
FieldInfo fi = obj.GetType().
  GetField("Name", BindingFlags.NonPublic | BindingFlags.Instance);
fi.SetValue(obj,value)

Con la reflexión, todo puede ser un libro abierto :) En mi ejemplo, estamos vinculados a un campo de nivel de instancia privada.

JoshBerke
fuente
8

Puede probar esto cuando desee asignar en masa propiedades de un Objeto desde otro Objeto usando los nombres de Propiedades:

public static void Assign(this object destination, object source)
    {
        if (destination is IEnumerable && source is IEnumerable)
        {
            var dest_enumerator = (destination as IEnumerable).GetEnumerator();
            var src_enumerator = (source as IEnumerable).GetEnumerator();
            while (dest_enumerator.MoveNext() && src_enumerator.MoveNext())
                dest_enumerator.Current.Assign(src_enumerator.Current);
        }
        else
        {
            var destProperties = destination.GetType().GetProperties();
            foreach (var sourceProperty in source.GetType().GetProperties())
            {
                foreach (var destProperty in destProperties)
                {
                    if (destProperty.Name == sourceProperty.Name && destProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType))
                    {
                        destProperty.SetValue(destination,     sourceProperty.GetValue(source, new object[] { }), new object[] { });
                        break;
            }
        }
    }
}
usuario3679106
fuente
2
Hola y bienvenido a Stack Overflow. Formatee su código correctamente y no solo lo descargue en su publicación, sino que ayudará a otros a comprender su respuesta.
ThreeFx
0

Acabo de publicar un paquete Nuget que permite configurar no solo las propiedades de primer nivel, sino también las propiedades anidadas en el objeto dado en cualquier profundidad.

Aquí esta el paquete

Establece el valor de una propiedad de un objeto por su ruta desde la raíz.

El objeto puede ser un objeto complejo y la propiedad puede ser una propiedad anidada profunda de varios niveles o puede ser una propiedad directamente debajo de la raíz. ObjectWriterencontrará la propiedad utilizando el parámetro de ruta de propiedad y actualizará su valor. La ruta de propiedad son los nombres adjuntos de las propiedades visitadas desde la raíz hasta la propiedad del nodo final que queremos establecer, delimitadas por el parámetro de cadena delimitador.

Uso:

Para configurar las propiedades directamente debajo de la raíz del objeto:

Es decir. LineItemla clase tiene una propiedad int llamadaItemId

LineItem lineItem = new LineItem();

ObjectWriter.Set(lineItem, "ItemId", 13, delimiter: null);

Para configurar la propiedad anidada en varios niveles debajo de la raíz del objeto:

Es decir. InviteLa clase tiene una propiedad llamada State, que tiene una propiedad llamada Invite(de tipo Invitar), que tiene una propiedad llamada Recipient, que tiene una propiedad llamadaId .

Para hacer las cosas aún más complejas, la Statepropiedad no es un tipo de referencia, es un struct.

Aquí es cómo puede establecer la propiedad Id (al valor de cadena de "perspectiva") en la parte inferior del árbol de objetos en una sola línea.

Invite invite = new Invite();

ObjectWriter.Set(invite, "State_Invite_Recipient_Id", "outlook", delimiter: "_");
Dogu Arslan
fuente
0

Basado en la sugerencia de MarcGravell, he construido el siguiente método estático: el método asigna genéricamente todas las propiedades coincidentes del objeto fuente al destino usando FastMember

 public static void DynamicPropertySet(object source, object target)
    {
        //SOURCE
        var src_accessor = TypeAccessor.Create(source.GetType());
        if (src_accessor == null)
        {
            throw new ApplicationException("Could not create accessor!");
        }
        var src_members = src_accessor.GetMembers();
        if (src_members == null)
        {
            throw new ApplicationException("Could not fetch members!");
        }
        var src_class_members = src_members.Where(x => x.Type.IsClass && !x.Type.IsPrimitive);
        var src_class_propNames = src_class_members.Select(x => x.Name);
        var src_propNames = src_members.Except(src_class_members).Select(x => x.Name);

        //TARGET
        var trg_accessor = TypeAccessor.Create(target.GetType());
        if (trg_accessor == null)
        {
            throw new ApplicationException("Could not create accessor!");
        }
        var trg_members = trg_accessor.GetMembers();
        if (trg_members == null)
        {
            throw new ApplicationException("Could not create accessor!");
        }
        var trg_class_members = trg_members.Where(x => x.Type.IsClass && !x.Type.IsPrimitive);
        var trg_class_propNames = trg_class_members.Select(x => x.Name);
        var trg_propNames = trg_members.Except(trg_class_members).Select(x => x.Name);



        var class_propNames = trg_class_propNames.Intersect(src_class_propNames);
        var propNames = trg_propNames.Intersect(src_propNames);

        foreach (var propName in propNames)
        {
            trg_accessor[target, propName] = src_accessor[source, propName];
        }
        foreach (var member in class_propNames)
        {
            var src = src_accessor[source, member];
            var trg = trg_accessor[target, member];
            if (src != null && trg != null)
            {
                DynamicPropertySet(src, trg);
            }
        }
    }
Convincente
fuente