【问题标题】:Test if Convert.ChangeType will work between two types测试 Convert.ChangeType 是否可以在两种类型之间工作
【发布时间】:2009-09-09 12:04:18
【问题描述】:

这是this question 关于使用反射转换值的后续内容。将某种类型的对象转换为另一种类型可以这样完成:

object convertedValue = Convert.ChangeType(value, targetType);

给定两个 Type 实例(比如 FromType 和 ToType),有没有办法测试转换是否成功?

例如我可以写一个这样的扩展方法吗:

public static class TypeExtensions
{
    public static bool CanChangeType(this Type fromType, Type toType)
    {
        // what to put here?
    }
}

编辑:这就是我现在所拥有的。丑,但我还没有看到其他方法......

bool CanChangeType(Type sourceType, Type targetType)
{
  try
  {
    var instanceOfSourceType = Activator.CreateInstance(sourceType);
    Convert.ChangeType(instanceOfSourceType, targetType);
    return true; // OK, it can be converted
  }
  catch (Exception ex)
  {
    return false;
  }

【问题讨论】:

  • 是的,我喜欢 Convert.TryChangeType 方法...
  • @Thomas:那很好,但这并不是我在这里所需要的。我还没有 fromType 的实例,只有 Type 本身。
  • 我认为您可以可靠检查的是 fromType 实现了IConvertible,但这并不能保证任何尝试的转换都会成功。
  • 我现在正在对通过 Activator.CreateInstance.Ugly 创建的实例执行实际的 ChangeType 方法,但目前我看不到其他方法...

标签: c# reflection


【解决方案1】:

我刚刚遇到同样的问题,我使用 Reflector 查看 ChangeType 的来源。 ChangeType在3种情况下抛出异常:

  1. conversionType 为空
  2. 值为空
  3. 值未实现 IConvertible

这3个勾选后,保证可以转换。因此,您只需自己检查这 3 件事,即可节省大量性能并移除 try{}/catch{} 块:

public static bool CanChangeType(object value, Type conversionType)
{
    if (conversionType == null)
    {
        return false;
    }

    if (value == null)
    {              
        return false;
    }

    IConvertible convertible = value as IConvertible;

    if (convertible == null)
    {
        return false;
    }

    return true;
}

【讨论】:

  • 很好的答案。应该自己考虑一下!
  • 谢谢。我遗漏了一张小支票,但这取决于你。如果 convertible == null,但 value 已经是您尝试转换的类型,那么您应该能够返回 true。
  • 请注意,它仍然可以抛出 InvalidCastException。似乎您对此无能为力。如果 IConvertible 有 CanConvertToType 方法就好了。
  • 对象实现了 IConvertible 并不意味着它可以将自己转换为任何类型。例如,System.Int32 实现了 IConvertible,但我不确定是否可以将任何 Int32 转换为非洲语言类的对象
  • 还有其他可能发生的异常——如果这两种类型通常是“兼容的”但源值不适合目标;例如。 Convert.ChangeType(double.MaxValue, typeof(int)) 会抛出 OverflowException。
【解决方案2】:

检查反射器中的方法 Convert.ChangeType 我在静态构造函数中发现了这个:

ConvertTypes = new Type[] { 
        typeof(Empty), typeof(object), typeof(DBNull), typeof(bool), typeof(char), typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal), 
        typeof(DateTime), typeof(object), typeof(string)
     };

最后,此方法只是检查源是否正在实现 IConvertible 或目标是否为上述 ConvertType 之一。所以你的方法应该看起来像这样(非常粗糙):

return (ConvertTypes.Contains(toType) || typeof(IConvertible).IsAssignableFrom(fromType));

【讨论】:

  • 类似于我在上面的评论中提到的。不幸的是,检查Type 实现IConvertible 是不够的。仍然无法保证任何尝试的转换都会真正成功。
  • 在我的场景中,我只关心这些特殊的原语。我真的只是想测试尝试ChangeType 是否明智,这是唯一合理的方法(try{...}catch{} 除外)
  • 可以说最好使用HashSet<Type> 而不是Type[],如果您希望在紧密循环中进行此检查以避免O(N) 在数组中搜索,这一点更为重要。致电BinarySearch() 也可能是一种选择
  • 再次,与其他答案一样,这完全是错误的。当目标和源类型相同时,Convert.ChangeType 不会抛出异常,无论它是否实现 IConvertible。当值为 null 且目标类型不是值类型时也不会抛出异常。
【解决方案3】:

我编写了一个小框架,其中包含一个 Convert 类,它可以做的比 System.Convert 类更多。如果您有兴趣使用它,可以从Github 下载。它无法确定您是否可以在值之间进行转换,但这似乎是一个值得添加的好功能。

它确实包括根据以下内容转换值的能力:

  • IConvertible
  • 类型转换器
  • ToXxx 方法
  • 解析静态方法
  • 参数化构造函数
  • 和其他一些小问题

Data Type Conversions

【讨论】:

  • 看起来很有趣。我去看看。
【解决方案4】:

基于已经回答的两个问题,我想出了这个,(需要 c#7)

public static object ChangeType(object value, Type conversion)
    {
        var type = conversion;

        if (type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
        {
            if (value == null)
            {
                return null;
            }

            type = Nullable.GetUnderlyingType(type);
        }

        return Convert.ChangeType(value, type);
    }

public static (bool IsSuccess, object Value) TryChangeType(object value, Type conversionType)
    {
        (bool IsSuccess, object Value) response = (false, null);
        var isNotConvertible = 
            conversionType == null 
                || value == null 
                || !(value is IConvertible)
            || !(value.GetType() == conversionType);
        if (isNotConvertible)
        {
            return response;
        }
        try
        {
            response = (true, ChangeType(value, conversionType));
        }
        catch (Exception)
        {
            response.Value = null;
        }

        return response;
    }
}

生产代码示例:

public Item()
    {
        foreach (var pinfo in GetType().GetProperties())
        {
            object value = 0;
            var response = ReflectionHelpers.TryChangeType(value, pinfo.PropertyType);
            if(response.IsSuccess)
            {
                pinfo.SetValue(this, response.Value);
            }
        }
    }

这会以 0 开始所有可能的属性。

【讨论】:

    猜你喜欢
    • 2019-05-17
    • 2020-01-27
    • 2019-04-23
    • 1970-01-01
    • 1970-01-01
    • 2015-01-30
    • 1970-01-01
    • 2014-08-30
    • 1970-01-01
    相关资源
    最近更新 更多