How do I perform explicit operation casting from reflection?

自古美人都是妖i 提交于 2019-12-05 02:18:38

Well its really no different from your non-reflection code, you still need to explicitly cast the number to a Foo:

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

Live example: http://rextester.com/rundotnet?code=BPQ74480

Out of interest I tried a few alternatives.

  1. Make it an implicit cast in Foo - doesnt work, same error Live
  2. Use Convert.ChangeType(17.17m,typeof(Foo)) - also doesnt work. Live

I looked at this question today while trying to copy fields by name between to objects. I was quite disappointed to see the selected answer was "you can only explicitly call an explicit operator." After all, anything else can be done by reflection.

My problem was a reflection method trying to make a deep copy between two classes because of a complex type. I tried to define an explicit operator conversion, but it didn't seem to get called, so I figured out a way to get it by reflection. Using some other research about calling static methods, I found this works for me when copying a complex type stored in pSource into a different type in property pDest. the type in pDest has a conversion from pSource's type.


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 is my destination instance. pDest is the current PropertyInfo in the destination instance.

source is my source instance. pSource is the current PropertyInfo in the source instance.

The type used for my destination property has an explicit conversion from the source property type, this works without needing any

I needed functionality like Ted H, but I implemented it like this:

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

Edit: I needed a more evolved version recently, and this is what I came up with. Be aware it does not cover all available conversions.

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

}
Michael Bray

Building on Herman's answer... I realized that both the source and destination class may define the conversion operator. So here is my version:

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;
}

Typical usage:

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

Note the following:

  • If the conversion is defined in both source and destination, the destination conversion operator method is given precedence
  • The function returns a bool indicating success/failure, and the actual converted value in an out variable (similar to TryParse methods)

Thanks all above for a great start on what I needed. I borrowed and also added. In my situation, I needed all of the above and also needed to search all ancestor base types for both the source and destination type to see if any of them contained an implicit or explicit conversion to my destination types. Adding this extra requirement I produced the below.

    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;
    }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!