2009-07-06 18 views
251

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 clase Ship, con una propiedad de Latitude, que es double.Establecer una propiedad por reflexión con un valor de cadena

Aquí es lo que me gusta hacer:

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

Como es, esto arroja una ArgumentException:

objeto de tipo 'System.String' no se puede convertir al tipo 'del sistema. Doble'.

¿Cómo puedo convertir el valor al tipo correcto, basado en propertyInfo?

+1

Pregunta para usted: ¿es esto parte de una solución personalizada de ORM? – user3308043

Respuesta

419

Puede usar Convert.ChangeType() - Le permite utilizar la información de tiempo de ejecución en cualquier tipo de IConvertible para cambiar los formatos de representación. Sin embargo, no todas las conversiones son posibles y es posible que deba escribir lógica de casos especiales si desea admitir conversiones de tipos que no son IConvertible.

El código correspondiente (sin manejo de excepciones o la lógica de casos especiales) 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); 
31

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

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

De hecho, le recomiendo que busque en toda la Convert Class.

Esta clase y muchas otras clases útiles son parte de System Namespace. Me resulta útil escanear ese espacio de nombres todos los años para ver qué funciones me he perdido. ¡Darle una oportunidad!

+1

El OP probablemente desee la respuesta general, para establecer una propiedad de cualquier tipo que tenga una conversión obvia de una cadena. –

+0

Buen punto. Editaré y señalaré los respondedores reales, o eliminaré los míos si alguien agrega lo que dije sobre el resto del espacio de nombres. –

3

o usted podría intentar:

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

//But this will cause problems if your string value IsNullOrEmplty... 
6

Probablemente usted está buscando el método Convert.ChangeType. 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); 
5

Usando Convert.ChangeType y obtener el tipo de convertir de la PropertyInfo.PropertyType.

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

¿Está buscando jugar con Reflection o está buscando construir un programa de producción de software? 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; 
+0

Debe respetar lo que las personas intentan hacer y no lo que usted piensa que deben hacer. Downvoted. (De 'GenericProgramming.exe: ReflectionBenefits()') –

18

noto una gran cantidad de personas están recomendando Convert.ChangeType - Esto funciona para algunos casos, sin embargo, tan pronto como se inicia la participación de nullable tipos usted comenzará a recibir InvalidCastExceptions:

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

Un envoltorio fue escrito hace unos años 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

11

Se puede utilizar 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 cuanto a la organización del código, se puede crear un kind-of mixin que daría lugar a un código como esto:

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

Esto podría ser ieved 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 pueden ser reutilizados para muchas clases diferentes.

También puede crear su propia type converters para adjuntar a sus propiedades o clases:

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

[TypeConverter(typeof(LatitudeConverter))] 
public class Latitude { ... } 
+0

¿Hay algún motivo en particular por el que haya agregado la interfaz del marcador en lugar de simplemente usar 'object'? – Groo

+0

Sí, la interfaz de marcador sirve como un marcador de posición para agregar los métodos de extensión. El uso de 'object' agregaría los métodos de extensión a todas las clases, lo que generalmente no es deseable. –

2

Si está escribiendo metro aplicación, se debe utilizar 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 lugar de

ship.GetType().GetProperty("Latitude"); 
3

Probé la respuesta desde LBushkin y funcionó muy bien, pero no funcionará para valores nulos y campos con posibilidad de nulos. Así que 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); 
} 
Cuestiones relacionadas