2011-09-08 10 views
10

Quiero usar la reflexión y hacer una cobertura implícita o explícita usando la reflexión.¿Cómo realizo operaciones explícitas desde la reflexión?

, yo les he definido Foo esta manera

public class Foo 
{ 
    public static explicit operator decimal(Foo foo) 
    { 
     return foo.Value; 
    } 

    public static explicit operator Foo(decimal number) 
    { 
     return new Foo(number); 
    } 

    public Foo() { } 

    public Foo(decimal number) 
    { 
     Value = number; 
    } 

    public decimal Value { get; set; } 

    public override string ToString() 
    { 
     return Value.ToString(); 
    } 
} 

Cuando ejecuto el código

decimal someNumber = 42.42m; 

var test = (Foo)someNumber; 

Console.WriteLine(test);  // Writes 42.42 No problems 

Cuando intento de definir una clase con Foo como un tipo de miembro y utilizar la reflexión para configurarlo. Obtengo la siguiente excepción.

Error  : Object of type 'System.Decimal' cannot be converted to type 'Foo'. 
StackTrace: at System.RuntimeType.TryChangeType(Object value, Binder binder, CultureInfo culture, Boolean needsSpecialCast) 
       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.RuntimePropertyInfo.SetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture) 
       at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, Object[] index) 

Este es el código que utilizo para establecer la propiedad con la reflexión

public class FooComposite 
{ 
    public Foo Bar { get; set; } 
} 

var properties = typeof(FooComposite).GetProperties(); 

var testFoo = new FooComposite(); 

foreach(var propertyInfo in properties) 
{ 
    propertyInfo.SetValue(testFoo, 17.17m, null); // Exception generated on this line 
} 

Console.WriteLine(testFoo.Bar); // Never gets here 

¿Cómo puedo hacer esta conversión?

+0

Si estás interesado en hacer esto de forma dinámica y no codificar el elenco, Marc hizo algo muy parecido a lo que busca en los foros de MSDN sobre el uso de expresiones (leer toda la discusión, no sólo la respuesta). Ver: http://social.msdn.microsoft.com/Forums/eu/csharplanguage/thread/c2a77a57-ebbb-4ac1-94c9-5287f01105ff – vcsjones

Respuesta

5

Bien es realmente diferente de su código no-reflexión, que todavía tienen que convierta explícitamente el número a una Foo:

propertyInfo.SetValue(testFoo,(Foo)17.17m, null); 

vivo ejemplo: http://rextester.com/rundotnet?code=BPQ74480

Fuera de interés probé unos alternativas.

  1. lo convierten en un implicit fundido en Foo - no funciona, el mismo error Live
  2. Uso Convert.ChangeType(17.17m,typeof(Foo)) - también no funciona. Live
+0

Esta es una buena publicación de blog que habla más sobre este tema del boxeo y el desempaquetado. http://philosopherdeveloper.wordpress.com/2010/05/05/the-difference-between-converting-and-unboxing-read-my-mind-compiler/ –

4

Miré a esta pregunta hoy al intentar copiar los campos por su nombre entre los objetos. Me decepcionó bastante ver que la respuesta seleccionada era "solo se puede llamar explícitamente a un operador explícito". Después de todo, cualquier cosa puede hacerse por reflexión.

Mi problema era un método de reflexión tratando de hacer una copia profunda entre dos clases debido a un tipo complejo. Traté de definir una conversión explicit operator, pero no pareció que se llamara, así que descubrí una forma de obtenerlo por reflexión. Usando otra investigación acerca de llamar a métodos estáticos, encontré que esto funciona para mí al copiar un tipo complejo almacenado en pSource en un tipo diferente en la propiedad pDest. el tipo en pDest tiene una conversión del tipo de pSource.


MethodInfo[] static_methods = pDest.PropertyType.GetMethods(System.Reflection.BindingFlags.Static | BindingFlags.Public); 
if (static_methods != null) 
{ 
    foreach (MethodInfo method in static_methods) 
    { 
     if(method.Name== "op_Explicit")      // this is a constant 
     {              // for explicit operators 
      ParameterInfo[] paramSet = method.GetParameters(); 
      if ((paramSet != null) && (paramSet.Length == 1)) 
      { 
       if (paramSet[0].ParameterType == pSource.PropertyType) // match the types! 
       { 
        pDest.SetValue(       // Destination prop 
         dstVar,        // Destination instance 
         method.Invoke(      // converter method 
           null,       // static has no 'this' 
           new object[] {     // value to convert from 
            pSource.GetValue(source, null) 
           }        // source property on 
                  // source instance 
         ) 
        ); // SetValue(...) 
       } 
      } 
     } 
    } 
} 

dstVar es mi instancia de destino. pDest es el PropertyInfo actual en la instancia de destino.

fuente es mi instancia de origen. pSource es el PropertyInfo actual en la instancia de origen.

El tipo usado para mi propiedad de destino tiene una conversión explícita del tipo de propiedad de origen, esto funciona sin necesidad de ningún

4

que necesitaba funcionalidad como Ted H, pero implementado de esta manera:

var cast = typeof(dest).GetMethod("op_Explicit", new Type[] { typeof(source) }); 
var result = cast.Invoke(null, new object[] {value}); 

Editar: Necesitaba una versión más evolucionada recientemente, y esto es lo que se me ocurrió. Tenga en cuenta que no cubre todas las conversiones disponibles.

private static object DynamicCast(object source, Type destType) { 
    Type srcType = source.GetType(); 
    if (srcType == destType) return source; 

    var paramTypes = new Type[] { srcType }; 
    MethodInfo cast = destType.GetMethod("op_Implicit", paramTypes); 

    if (cast == null) { 
     cast = destType.GetMethod("op_Explicit", paramTypes); 
    } 

    if (cast != null) return cast.Invoke(null, new object[] { source }); 

    if (destType.IsEnum) return Enum.ToObject(destType, source); 

    throw new InvalidCastException(); 

} 
+0

Tenga en cuenta que tanto el tipo de origen como el de destino pueden permitir la conversión operadores. Entonces, por ejemplo, si tiene una conversión explícita de A a B, entonces esto puede definirse en la clase A o la clase B, o ambas. –

+0

Creo que podría haberlo implementado así intencionalmente. Tal vez para que coincida con la forma en que funciona en el código C# estático, pero no estoy seguro de eso. Si realmente desea realizar conversiones extensas, también puede consultar las clases 'Convert' y' TypeConverter''s. – Herman

1

Sobre la base de Herman's answer ... me di cuenta que tanto el origen como el destino de clase pueden definir el operador de conversión. Así que aquí está mi versión:

private static bool DynamicCast(object source, Type destType, out object result) 
{ 
    Type srcType = source.GetType(); 
    if (srcType == destType) { result = source; return true; } 
    result = null; 

    BindingFlags bf = BindingFlags.Static | BindingFlags.Public; 
    MethodInfo castOperator = destType.GetMethods(bf) 
           .Union(srcType.GetMethods(bf)) 
           .Where(mi => mi.Name == "op_Explicit" || mi.Name == "op_Implicit") 
           .Where(mi => 
           { 
            var pars = mi.GetParameters(); 
            return pars.Length == 1 && pars[0].ParameterType == srcType; 
           }) 
           .Where(mi => mi.ReturnType == destType) 
           .FirstOrDefault(); 
    if (castOperator != null) result = castOperator.Invoke(null, new object[] { source }); 
    else return false; 
    return true; 
} 

uso típico:

object a = new A(); 
object o; 
if (DynamicCast(a, typeof(B), out o)) 
{ 
    B b = (B)o; 
    ... 
} 

en cuenta lo siguiente:

  • Si la conversión se define en la fuente y el destino, el método operador de conversión de destino es dada precedencia
  • La función devuelve un bool que indica éxito/falla, y el valor real convertido en un out variable (similar a los métodos TryParse)
0

Gracias a todos por encima de un gran comienzo en lo que necesitaba. Pedí prestado y también lo agregué. En mi situación, necesitaba todo lo anterior y también necesitaba buscar todos los tipos de antecesor para el tipo de origen y de destino para ver si alguno de ellos contenía una conversión implícita o explícita a mis tipos de destino. Al agregar este requerimiento adicional, produje el siguiente.

private static bool TryCast(object source, Type destType, out object result) 
    { 
     Type srcType = source.GetType(); 
     if (srcType == destType) 
     { 
      result = source; 
      return true; 
     } 

     MethodInfo cast = null; 
     while (cast == null && srcType != typeof(object)) 
     { 
      cast = GetCastMethod(srcType, srcType, destType); 
      if (cast == null) cast = GetCastMethod(destType, srcType, destType); 
      srcType = srcType.BaseType; 
     } 

     if (cast != null) 
     { 
      result = cast.Invoke(null, new object[] { source }); 
      return true; 
     } 

     if (destType.IsEnum) 
     { 
      result = Enum.ToObject(destType, source); 
      return true; 
     } 

     result = null; 
     return false; 
    } 

    private static MethodInfo GetCastMethod(Type typeWithMethod, Type srcType, Type destType) 
    { 
     while (typeWithMethod != typeof(object)) 
     { 
      foreach (MethodInfo method in typeWithMethod.GetMethods(BindingFlags.Static | BindingFlags.Public)) 
      { 
       if (method.ReturnType == destType && (method.Name == "op_Explicit" || method.Name == "op_Implicit")) 
       { 
        ParameterInfo[] parms = method.GetParameters(); 
        if (parms != null && parms.Length == 1 && parms[0].ParameterType == srcType) 
         return method; 
       } 
      } 
      typeWithMethod = typeWithMethod.BaseType; 
     } 

     return null; 
    } 
Cuestiones relacionadas