Pregunta

Quiero utilizar la reflexión y el hacer, ya sea implícita o explícita coversion el uso de la reflexión.

Dado que se ha 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 este código

decimal someNumber = 42.42m;

var test = (Foo)someNumber;

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

Cuando trato de definir una clase con Foo como un tipo de miembro y el uso de la reflexión para establecer la misma.Tengo 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 uso 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?

¿Fue útil?

Solución

Bueno, realmente no es diferente de su código de no reflexión, aún necesita lanzar explícitamente el número a un Foo:

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

Ejemplo en vivo: http://rextester.com/rundotnet?code=bpq74480

Por interés probé algunas alternativas.

  1. Hazlo un implicit echar Foo - no funciona, el mismo error Vivir
  2. Usar Convert.ChangeType(17.17m,typeof(Foo)) - tampoco funciona. Vivir

Otros consejos

Miré esta pregunta hoy mientras intentaba copiar campos por nombre entre los objetos. Me decepcionó bastante ver que la respuesta seleccionada fue "Solo puedes llamar explícitamente a un operador explícito". Después de todo, cualquier otra cosa se puede hacer 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 un explicit operator Conversión, pero no parecía ser llamado, así que descubrí una forma de obtenerlo mediante la reflexión. Utilizando alguna otra investigación sobre llamar a los 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 la propiedad actualinfo en la instancia de destino.

La fuente es mi instancia de origen. PSOURCE es la propiedad actualinfo en la instancia de origen.

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

Necesitaba funcionalidad como Ted H, pero lo implementé así:

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

}

Sobre la base de Respuesta de Herman... Me di cuenta de que tanto la clase de origen como de destino 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;
    ...
}

Tenga en cuenta lo siguiente:

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

Gracias a todos los anteriormente para un gran comienzo en lo que yo necesitaba.Me prestaron y también agregó.En mi situación, tenía todo lo anterior, y también se necesita para buscar en todos los antepasado tipos de base para el origen y el tipo de destino para ver si alguno de ellos contenía una conversión implícita o explícita a mi destino, tipos.La adición de este requisito adicional que produce 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;
    }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top