【问题标题】:How can I use reflection to convert from int to decimal?如何使用反射将 int 转换为 decimal?
【发布时间】:2011-05-01 14:57:50
【问题描述】:

我有一些看起来像这样的代码(运行良好):

        int integer = 42;
        decimal? castTo = integer;

然后我想用反射做一些类似的事情,一些代码看起来像这样:

object value = source; // source was an int originally
var parameters = new object[1];    
    ...
    parameters[0] = value;
    var setMethod = property.GetSetMethod();     
    // Call the set method, which takes a decimal? as a parameter
    setMethod.Invoke(o, parameters);  

当我这样做时,我得到:

failed: System.ArgumentException : Object of type 'System.Int32' cannot be converted to type 'System.Nullable`1[System.Decimal]'.
    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.MethodBase.Invoke(Object obj, Object[] parameters)

为什么在其他地方可以正常工作的隐式类型转换会因反射而失败?使用反射来执行这种转换有技巧吗?


编辑:感谢大家的回复。这是我根据答案提出的解决方案:

    private object Convert(object source, Type destinationType)
    {
        if (source == null)
        {
            return null;
        }

        var sourceType = source.GetType();

        // unwrap nullable types
        var nullableType = Nullable.GetUnderlyingType(destinationType);
        if(nullableType != null)
        {
            destinationType = nullableType;
        }

        nullableType = Nullable.GetUnderlyingType(sourceType);
        if(nullableType != null)
        {
            sourceType = nullableType;
        }


        var implicitCastMethod =
            destinationType.GetMethod("op_Implicit", 
                                 new[] { sourceType } );

        if(implicitCastMethod == null)
        {
            return null;
        }

        return implicitCastMethod.Invoke(null, new[] { source });
    }

编辑#2:我希望有人提到System.Convert.ChangeType(),它处理这些情况等等。事实证明,op_Implicit 只能转换为限制较少的数字类型。 (当然,因此名称中包含“隐式”)。换句话说,第一个解决方案适用于intdecimal?,但不适用于decimal?int。 (如果隐式转换失败,如果我希望能够处理从decimal? 回到int 的转换,我似乎需要更改此代码以尝试op_Explicit。)

由于System.Convert.ChangeType() 不适用于Nullable<> 类型,我最终使用了一些类似于我发现的here 的代码(稍作修改):

    private static object Convert(object source, Type destinationType)
    {
        if(destinationType == null)
        {
            throw new ArgumentNullException("destinationType");
        }

        if(destinationType.IsGenericType && 
            destinationType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
        {
            if (source == null)
            {
                return null;
            }
            destinationType = Nullable.GetUnderlyingType(destinationType);                
        }

        return System.Convert.ChangeType(source, destinationType);


    }

【问题讨论】:

    标签: c# reflection type-conversion


    【解决方案1】:

    您必须自己进行转换,因为编译器在非反射环境中处理转换。由于反射代码基本上像编译器一样评估类型和对象,因此您必须在对象上查找名为 op_implicit 的方法并调用所需的参数(在您的情况下为 Int32)并调用它。之后,您可以调用属性访问器。一种可能的方法如下:

    //search for an implicit cast operator on the target type
    MethodInfo[] methods = targetType.GetMethods();
    foreach(MethodInfo method = source.GetType().GetMethod("op_Implicit"))
    {
      if (method.Name == "op_Implicit")
      {
        ParameterInfo[] parameters = method.GetParameters();
        if (parameters.Length == 1 && parameters[0].ParameterType == value.GetType())
        {
          value = method.Invoke(obj,new object[]{value});
          break;
        }
      }
    }
    

    【讨论】:

    • 这基本上对我有用。至少它让我走上了正轨。谢谢!
    • MethodInfo method = source.GetType().GetMethod("op_Implicit"); 比显式循环搜索更容易阅读。
    • @Merlyn,是的,如果您阅读我的第一次编辑,您会注意到这就是我所做的,除了我还将类型添加到 GetMethod() 调用中。否则如果有多个重载的 op_Implicit 方法,我想我还是得循环?
    • @Mike:哎呀——你是对的。我猜你的另一个选择是使用 LINQ,如果你喜欢的话。
    【解决方案2】:

    其他答案已经涵盖了为什么隐式转换不起作用。如果您需要隐式转换,请使用其他答案之一。如果您实际上不需要隐式转换,这里有一个更简单的选项:

    class Test
    {
      public decimal? Val { get; set; }
    }
    
    class Program
    {
      static void Main(string[] args)
      {
        object o = new Test();
        object source = 5;
        var setMethod = typeof(Test).GetProperty("Val").GetSetMethod();
        // Just do the cast explicitly
        setMethod.Invoke(o, new object[] { (decimal?)(int)source });
      }
    }
    

    请注意,如果您缺少 (decimal?) 演员表,则会收到原始问题引用的错误。如果您缺少 (int) 演员表,您会收到以下错误:

    Unhandled Exception: System.InvalidCastException: Specified cast is not valid.
       at Program.Main(String[] args) in ...\ConsoleApplication1\Program.cs:line 14
    

    【讨论】:

    • 新奇想法;这至少让我发笑。我想我应该在问题中这么说,但真正的目标是让它充满活力;我并不总是在做(十进制?)(int)。 =)
    • @Mike:我想的差不多,但你无法通过互联网上的一个问题来衡量经验。对于新手来说,您必须投 两次 才能使其正常工作,这并不是 100% 显而易见的。我会留下这个答案,以防它对技能较低但仍然遇到相同错误的人有用。
    【解决方案3】:

    运行时不知道隐式转换。

    您可以通过反射调用op_Implicit 或其他转换方法,但这样您只能获得您实现的特定转换语义。如果您使用的是 C# 4.0,我建议在这里使用“动态”类型,因为它会自动实现 C# 转换语义。

    【讨论】:

    • 谢谢。我们使用的是 C# 3.5,但我一直看到像这样的 C# 4.0 功能看起来很方便。
    【解决方案4】:

    为什么要进行隐式类型转换 在其他地方工作正常失败 反思?

    因为那里没有转换。隐式转换并不意味着它会自动发生,当您在代码中使用它时,编译器会为其添加代码。

    如果你想在反射中使用它,你必须这样做,即找到从一种类型转换到另一种类型的静态方法,然后调用它。

    【讨论】:

    • 感谢您的解释。我想我希望反射有一种更简单的方法来做到这一点,但是唉。
    猜你喜欢
    • 2021-08-31
    • 2020-10-14
    • 2017-06-22
    • 1970-01-01
    • 1970-01-01
    • 2014-09-16
    • 2010-11-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多