How do I perform explicit operation casting from reflection?

安稳与你 提交于 2020-01-13 08:02:50

问题


I want to use reflection and do either an implicit or explicit coversion using reflection.

Given I have defined Foo this way

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

When I run this code

decimal someNumber = 42.42m;

var test = (Foo)someNumber;

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

When I try to define a class with Foo as a member type and use reflection to set it. I get the following Exception.

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)

Here is the code I use to set the property with reflection

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

How can I do this conversion?


回答1:


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



回答2:


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




回答3:


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

}



回答4:


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)



回答5:


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


来源:https://stackoverflow.com/questions/7351289/how-do-i-perform-explicit-operation-casting-from-reflection

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