【问题标题】:How do I perform explicit operation casting from reflection?如何从反射执行显式操作转换?
【发布时间】:2011-11-13 03:46:22
【问题描述】:

我想使用反射并使用反射进行隐式或显式覆盖。

鉴于我已经这样定义了 Foo

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

当我运行这段代码时

decimal someNumber = 42.42m;

var test = (Foo)someNumber;

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

当我尝试使用 Foo 定义一个类作为成员类型并使用反射来设置它时。我得到以下异常。

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)

这是我用来设置反射属性的代码

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# operators implicit-conversion explicit-conversion


【解决方案1】:

任何事情都可以通过反思来完成。

我的问题是由于复杂类型而试图在两个类之间进行深度复制的反射方法。我试图定义一个explicit operator 转换,但它似乎没有被调用,所以我想出了一种通过反射得到它的方法。使用其他一些关于调用静态方法的研究,我发现当将存储在 pSource 中的复杂类型复制到属性 pDest 中的不同类型时,这对我有用。 pDest 中的类型是从 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 是我的目标实例。 pDest 是目标实例中的当前 PropertyInfo。

source 是我的源实例。 pSource 是源实例中的当前 PropertyInfo。

用于我的目标属性的类型具有从源属性类型的显式转换,这无需任何操作

【讨论】:

    【解决方案2】:

    感谢以上所有内容,让我开始了我所需要的一切。我借了也加了。在我的情况下,我需要上述所有内容,还需要搜索所有祖先基类型以查找源类型和目标类型,以查看它们中的任何一个是否包含到我的目标类型的隐式或显式转换。添加这个额外的要求,我产生了以下内容。

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

    【讨论】:

      【解决方案3】:

      我需要像 Ted H 这​​样的功能,但我是这样实现的:

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

      编辑:我最近需要一个更进化的版本,这就是我想出的。请注意,它并未涵盖所有可用的转化。

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

      【讨论】:

      • 请注意,源类型和目标类型都可能允许转换运算符。因此,例如,如果您有从 A 到 B 的显式转换,那么这可能在 A 类或 B 类中定义,或两者兼而有之。
      • 我想我可能是故意这样实现的。也许这样它与它在静态 c# 代码中的工作方式相匹配,但我不确定。如果你真的想做大量的转换,你可能还想看看Convert 类和TypeConverter 的。
      【解决方案4】:

      基于Herman's answer...我意识到源类和目标类都可以定义转换运算符。所以这是我的版本:

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

      典型用法:

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

      注意以下几点:

      • 如果在源和目标中都定义了转换,则目标转换运算符方法优先
      • 该函数返回一个表示成功/失败的布尔值,以及out 变量中的实际转换值(类似于 TryParse 方法)

      【讨论】:

        【解决方案5】:

        嗯,它与您的非反射代码确实没有什么不同,您仍然需要将数字显式转换为Foo

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

        现场示例:http://rextester.com/rundotnet?code=BPQ74480

        出于兴趣,我尝试了一些替代方案。

        1. Foo 中将其设为implicit - 不起作用,同样的错误Live
        2. 使用Convert.ChangeType(17.17m,typeof(Foo)) - 也不起作用。 Live

        【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-07-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多