Establecer la propiedad del objeto usando la reflexión

¿Hay alguna manera en C # 3.5 donde puedo usar la reflexión para establecer una propiedad del objeto?

Ex:

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

Quiero establecer obj.Name con reflection. Algo como:

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

¿Hay alguna forma de hacer esto?

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 lanzará una excepción si obj no tiene una propiedad llamada Name , o 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 pueda establecer:

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

También puedes hacer:

 Type type = target.GetType(); PropertyInfo prop = type.GetProperty("propertyName"); prop.SetValue (target, propertyValue, null); 

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

Reflexión, básicamente, es decir

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

o hay bibliotecas para ayudar a ambos en términos de conveniencia y 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 se trata de un campo frente a una propiedad)

O podrías envolver el único delineador de Marc dentro de tu 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"); 

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

Sí, usando System.Reflection :

 using System.Reflection; ... string prop = "name"; PropertyInfo pi = myObject.GetType().GetProperty(prop); pi.SetValue(myObject, "Bob", null); 

También puede acceder a los campos de una 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.

Usa algunas cosas como esta:

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

Puede probar esto cuando quiera asignar en masa propiedades de un Objeto de otro Objeto utilizando 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; } } } } 

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

Aquí está 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 propiedad anidada profunda de niveles múltiples o puede ser una propiedad directamente debajo de la raíz. ObjectWriter buscará la propiedad utilizando el parámetro de ruta de propiedad y actualizará su valor. La ruta de la propiedad es el nombre adjunto de las propiedades visitadas desde la raíz hasta la propiedad del nodo final que queremos establecer, delimitada por el parámetro de la cadena del delimitador.

Uso:

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

Es decir. LineItem clase LineItem tiene una propiedad int llamada ItemId

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

Para configurar múltiples propiedades anidadas debajo de la raíz del objeto:

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

Para hacer las cosas aún más complejas, la propiedad de State no es un tipo de referencia, es una 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: "_"); 

Basado en la sugerencia de MarcGravell, he construido el siguiente método estático. El método genéricamente asigna todas las propiedades coincidentes desde el objeto de origen al destino utilizando 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); } } }