2010-10-28 7 views
11

que tienen algo de código (que funciona bien) que se ve algo como esto:¿Cómo puedo usar la reflexión para convertir de int a decimal?

 int integer = 42; 
     decimal? castTo = integer; 

luego quería hacer algo similar con la reflexión, con algo de código que tiene este aspecto:

object value = source; // source was an int originally 
var parameters = new object[1];  
    ... 
    parameters[0] = value; 
    var setMethod = property.GetSetMethod();  
    // Call the set method, which takes a decimal? as a parameter 
    setMethod.Invoke(o, parameters); 

Cuando Hago esto, obtengo:

failed: System.ArgumentException : Object of type 'System.Int32' cannot be converted to type 'System.Nullable`1[System.Decimal]'. 
    at System.RuntimeType.CheckValue(Object value, Binder binder, CultureInfo culture, BindingFlags invokeAttr) 
    at System.Reflection.MethodBase.CheckArguments(Object[] parameters, Binder binder, BindingFlags invokeAttr, CultureInfo culture, Signature sig) 
    at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks) 
    at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) 
    at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters) 

¿Por qué una conversión de tipo implícito que funciona bien en otro lugar falla con la reflexión? ¿Hay algún truco para usar la reflexión para realizar esta conversión?


Editar: Gracias a todos por las respuestas. Aquí está la solución que se me ocurrió, sobre la base de las respuestas:

private object Convert(object source, Type destinationType) 
    { 
     if (source == null) 
     { 
      return null; 
     } 

     var sourceType = source.GetType(); 

     // unwrap nullable types 
     var nullableType = Nullable.GetUnderlyingType(destinationType); 
     if(nullableType != null) 
     { 
      destinationType = nullableType; 
     } 

     nullableType = Nullable.GetUnderlyingType(sourceType); 
     if(nullableType != null) 
     { 
      sourceType = nullableType; 
     } 


     var implicitCastMethod = 
      destinationType.GetMethod("op_Implicit", 
           new[] { sourceType }); 

     if(implicitCastMethod == null) 
     { 
      return null; 
     } 

     return implicitCastMethod.Invoke(null, new[] { source }); 
    } 

Edición # 2: Me gustaría que alguien había mencionado System.Convert.ChangeType(), que se ocupa de estos casos, y más. Resulta que op_Implicit solo puede convertir a tipos numéricos menos restrictivos. (por supuesto, de ahí el "implícito" en el nombre). En otras palabras, la primera solución funcionó para intdecimal? pero no decimal?int. (Parece que necesitaría para cambiar el código de probar también op_Explicit si la conversión implícita falló, si quería ser capaz de manejar una conversión de decimal? de nuevo a int.)

Desde System.Convert.ChangeType() no funciona con Nullable<> tipos, que finalmente acabaron utilizando un código similar a lo que he encontrado here (ligeramente modificada):

private static object Convert(object source, Type destinationType) 
    { 
     if(destinationType == null) 
     { 
      throw new ArgumentNullException("destinationType"); 
     } 

     if(destinationType.IsGenericType && 
      destinationType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) 
     { 
      if (source == null) 
      { 
       return null; 
      } 
      destinationType = Nullable.GetUnderlyingType(destinationType);     
     } 

     return System.Convert.ChangeType(source, destinationType); 


    } 

Respuesta

5

Usted tendrá que hacer la conversión a sí mismo, ya que el compilador se encarga del reparto en un entorno no-reflexión. Como el código de reflexión básicamente evalúa tipos y objetos como lo hace el compilador, tendrá que buscar un método llamado op_implicit con los parámetros necesarios (en su caso Int32) en su objeto e invocarlo. Después de eso, puede invocar el acceso a la propiedad. Una forma posible sería la siguiente:

//search for an implicit cast operator on the target type 
MethodInfo[] methods = targetType.GetMethods(); 
foreach(MethodInfo method = source.GetType().GetMethod("op_Implicit")) 
{ 
    if (method.Name == "op_Implicit") 
    { 
    ParameterInfo[] parameters = method.GetParameters(); 
    if (parameters.Length == 1 && parameters[0].ParameterType == value.GetType()) 
    { 
     value = method.Invoke(obj,new object[]{value}); 
     break; 
    } 
    } 
} 
+0

Esto es esencialmente lo que funcionó para mí. Al menos me puso en el camino correcto. ¡Gracias! – mpontillo

+0

'MethodInfo method = source.GetType(). GetMethod (" op_Implicit ");' es más fácil de leer que la búsqueda en bucle explícita. –

+0

@Merlyn, sí, notará que eso es lo que hice si lee mi primera edición, excepto que también agregué el tipo en la llamada GetMethod() también. De lo contrario, si hubiera varios métodos op_Implicit sobrecargados, creo que todavía tendría que hacer un bucle? – mpontillo

2

Why would an implicit type conversion that works fine elsewhere fail with reflection?

Debido a que no hay conversión allí. La conversión implícita no significa que suceda automáticamente, cuando la usa en código el compilador agrega el código para ella.

Si desea utilizarlo en reflexión, debe hacer lo mismo, es decir, encuentre el método estático que realiza la conversión de un tipo al otro y llámelo.

+0

Gracias por la explicación. Supongo que esperaba que la reflexión tuviera una forma más fácil de hacerlo, pero, ¡ay! – mpontillo

5

El tiempo de ejecución no conoce las conversiones implícitas.

Puede llamar al op_Implicit u otro método de conversión a través de la reflexión, pero de esa manera solo obtendrá la semántica de conversión específica que implemente. Si está usando C# 4.0, le recomendaría usar el tipo "dinámico" aquí, ya que implementará la semántica de conversión de C# automáticamente.

+3

+1 para C# 4.0 recomendación 'dinámica' –

+0

Gracias por esto. Estamos usando C# 3.5, pero sigo viendo las características de C# 4.0 como esta que parecen prácticas. – mpontillo

1

Otras respuestas ya han cubierto por qué la conversión implícita no está funcionando. Si necesita que su conversión sea implícita, utilice una de las otras respuestas. En caso de que en realidad no necesita la conversión a ser implícita, aquí es una opción más simple:

class Test 
{ 
    public decimal? Val { get; set; } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
    object o = new Test(); 
    object source = 5; 
    var setMethod = typeof(Test).GetProperty("Val").GetSetMethod(); 
    // Just do the cast explicitly 
    setMethod.Invoke(o, new object[] { (decimal?)(int)source }); 
    } 
} 

Tenga en cuenta que si te falta el elenco (decimal?), se obtiene el error de que la pregunta original citado. Si le falta el modelo (int), obtiene este error:

Unhandled Exception: System.InvalidCastException: Specified cast is not valid. 
    at Program.Main(String[] args) in ...\ConsoleApplication1\Program.cs:line 14 
+0

idea nueva; me hizo reír por lo menos. Creo que debería haber dicho esto en la pregunta, pero el verdadero objetivo era hacer esto dinámico; No siempre estoy haciendo (decimal?) (Int). =) – mpontillo

+0

@Mike: pensé que sí, pero no se puede medir la experiencia con una sola pregunta en internet. No sería 100% obvio para un novato que tiene que emitir * dos veces * para que esto funcione. Dejaré esta respuesta, en caso de que sea útil para alguien menos calificado, que todavía encuentre el mismo error. –

Cuestiones relacionadas