Establecer una propiedad por reflexión con un valor de cadena

312

Me gustaría establecer una propiedad de un objeto a través de Reflection, con un valor de tipo string. Entonces, por ejemplo, supongamos que tengo una Shipclase, con una propiedad de Latitude, que es a double.

Esto es lo que me gustaría hacer:

Ship ship = new Ship();
string value = "5.5";
PropertyInfo propertyInfo = ship.GetType().GetProperty("Latitude");
propertyInfo.SetValue(ship, value, null);

Como es, esto arroja un ArgumentException:

El objeto del tipo 'System.String' no se puede convertir al tipo 'System.Double'.

¿Cómo puedo convertir el valor al tipo adecuado, según propertyInfo?

David Hodgson
fuente
1
Pregunta para usted: ¿forma parte de una solución ORM personalizada?
user3308043

Respuestas:

527

Puede usar Convert.ChangeType(): le permite usar información de tiempo de ejecución en cualquier IConvertibletipo para cambiar los formatos de representación. Sin embargo, no todas las conversiones son posibles, y es posible que deba escribir una lógica de caso especial si desea admitir conversiones de tipos que no lo son IConvertible.

El código correspondiente (sin manejo de excepciones o lógica de caso especial) sería:

Ship ship = new Ship();
string value = "5.5";
PropertyInfo propertyInfo = ship.GetType().GetProperty("Latitude");
propertyInfo.SetValue(ship, Convert.ChangeType(value, propertyInfo.PropertyType), null);
LBushkin
fuente
Revise la respuesta de @AliKaraca a continuación. Tanto este como el siguiente son rápidos y sueltos, pero hacen el trabajo para los tipos comunes.
Aaron Hudon
¿Hay un TryChangeTypeo CanChangeType?
Shimmy Weitzhandler el
34

Como han dicho varios otros, desea utilizar Convert.ChangeType:

propertyInfo.SetValue(ship,
    Convert.ChangeType(value, propertyInfo.PropertyType),
    null);

De hecho, te recomiendo que mires a toda la Convertclase .

Esta clase y muchas otras clases útiles son parte del Systemespacio de nombres . Me resulta útil escanear ese espacio de nombres cada año más o menos para ver qué características me he perdido. ¡Darle una oportunidad!

John Saunders
fuente
1
El OP probablemente quiera la respuesta general, para establecer una propiedad de cualquier tipo que tenga una conversión obvia de una cadena.
Daniel Earwicker el
Buen punto. Editaré y señalaré las respuestas reales, o eliminaré la mía si alguien agrega lo que dije sobre el resto del espacio de nombres.
John Saunders el
19

Noté que mucha gente recomienda Convert.ChangeType: esto funciona para algunos casos, sin embargo, tan pronto como comience a involucrar nullabletipos, comenzará a recibir InvalidCastExceptions:

http://weblogs.asp.net/pjohnson/archive/2006/02/07/Convert.ChangeType-doesn_2700_t-handle-nullables.aspx

Hace unos años se escribió un contenedor para manejar esto, pero eso tampoco es perfecto.

http://weblogs.asp.net/pjohnson/archive/2006/02/07/Convert.ChangeType-doesn_2700_t-handle-nullables.aspx

Tableta
fuente
13

Intenté la respuesta de LBushkin y funcionó muy bien, pero no funcionará para valores nulos y campos anulables. Entonces lo he cambiado a esto:

propertyName= "Latitude";
PropertyInfo propertyInfo = ship.GetType().GetProperty(propertyName);
if (propertyInfo != null)
{
     Type t = Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType;
     object safeValue = (value == null) ? null : Convert.ChangeType(value, t);
     propertyInfo.SetValue(ship, safeValue, null);
}
Ashkan Sirous
fuente
Tengo que decir gracias cuando conocí este caso y esta es la única solución. gracias ~!
Franva
11

Puede usar un convertidor de tipos (sin comprobación de errores):

Ship ship = new Ship();
string value = "5.5";
var property = ship.GetType().GetProperty("Latitude");
var convertedValue = property.Converter.ConvertFrom(value);
property.SetValue(self, convertedValue);

En términos de organizar el código, podría crear un tipo de mixin que resultaría en un código como este:

Ship ship = new Ship();
ship.SetPropertyAsString("Latitude", "5.5");

Esto se lograría con este código:

public interface MPropertyAsStringSettable { }
public static class PropertyAsStringSettable {
  public static void SetPropertyAsString(
    this MPropertyAsStringSettable self, string propertyName, string value) {
    var property = TypeDescriptor.GetProperties(self)[propertyName];
    var convertedValue = property.Converter.ConvertFrom(value);
    property.SetValue(self, convertedValue);
  }
}

public class Ship : MPropertyAsStringSettable {
  public double Latitude { get; set; }
  // ...
}

MPropertyAsStringSettable Se puede reutilizar para muchas clases diferentes.

También puede crear sus propios convertidores de tipos personalizados para adjuntarlos a sus propiedades o clases:

public class Ship : MPropertyAsStringSettable {
  public Latitude Latitude { get; set; }
  // ...
}

[TypeConverter(typeof(LatitudeConverter))]
public class Latitude { ... }
Jordão
fuente
¿Hay alguna razón en particular por la que agregaste la superficie del marcador en lugar de solo usarla object?
Groo
1
Sí, la interfaz del marcador sirve como marcador de posición para agregar los métodos de extensión. El uso objectagregaría los métodos de extensión a todas las clases, lo que generalmente no es deseable.
Jordão
6

Probablemente estés buscando el Convert.ChangeTypemétodo. Por ejemplo:

Ship ship = new Ship();
string value = "5.5";
PropertyInfo propertyInfo = ship.GetType().GetProperty("Latitude");
propertyInfo.SetValue(ship, Convert.ChangeType(value, propertyInfo.PropertyType), null);
John Calsbeek
fuente
5

Usar Convert.ChangeTypey obtener el tipo para convertir desde PropertyInfo.PropertyType.

propertyInfo.SetValue( ship,
                       Convert.ChangeType( value, propertyInfo.PropertyType ),
                       null );
tvanfosson
fuente
4

Contestaré esto con una respuesta general. Por lo general, estas respuestas no funcionan con guías. Aquí hay una versión funcional con guías también.

var stringVal="6e3ba183-89d9-e611-80c2-00155dcfb231"; // guid value as string to set
var prop = obj.GetType().GetProperty("FooGuidProperty"); // property to be setted
var propType = prop.PropertyType;

// var will be type of guid here
var valWithRealType = TypeDescriptor.GetConverter(propType).ConvertFrom(stringVal); 
Ali Karaca
fuente
1
Esta debería ser la respuesta aceptada. También funciona con GUID <3. Gracias, Ali (ese es el apodo de mi hija)
Cătălin Rădoi
3

O podrías probar:

propertyInfo.SetValue(ship, Convert.ChangeType(value, propertyInfo.PropertyType), null);

//But this will cause problems if your string value IsNullOrEmplty...
bytebender
fuente
2

Si está escribiendo la aplicación Metro, debe usar otro código:

Ship ship = new Ship();
string value = "5.5";
PropertyInfo propertyInfo = ship.GetType().GetTypeInfo().GetDeclaredProperty("Latitude");
propertyInfo.SetValue(ship, Convert.ChangeType(value, propertyInfo.PropertyType));

Nota:

ship.GetType().GetTypeInfo().GetDeclaredProperty("Latitude");

en vez de

ship.GetType().GetProperty("Latitude");
Serhiy
fuente
0

El uso del siguiente código debería resolver su problema:

item.SetProperty(prop.Name, Convert.ChangeType(item.GetProperty(prop.Name).ToString().Trim(), prop.PropertyType));
Marco Sotto
fuente
-9

¿Estás buscando jugar con Reflection o estás buscando construir un software de producción? Me preguntaría por qué estás usando la reflexión para establecer una propiedad.

Double new_latitude;

Double.TryParse (value, out new_latitude);
ship.Latitude = new_latitude;
clemahieu
fuente
1
Debe respetar lo que la gente intenta hacer y no lo que cree que debe hacer. Voto negativo (De GenericProgramming.exe:ReflectionBenefits())
Петър Петров