. net中的智能类型转换

本文关键字:类型转换 智能 net | 更新日期: 2023-09-27 18:01:45

我一直在尝试将。net中的字符串转换为值类型,其中结果值类型是未知的。我在代码中遇到的问题是,我需要一个接受字符串的方法,并使用"最佳匹配"方法填充结果值类型。如果该机制没有找到合适的匹配,则返回字符串。

这是我想出来的:

public static dynamic ConvertToType(string value)
{
    Type[] types = new Type[]
    {
        typeof(System.SByte),
        typeof(System.Byte), 
        typeof(System.Int16), 
        typeof(System.UInt16), 
        typeof(System.Int32), 
        typeof(System.UInt32), 
        typeof(System.Int64), 
        typeof(System.UInt64), 
        typeof(System.Single), 
        typeof(System.Double), 
        typeof(System.Decimal),
        typeof(System.DateTime),
        typeof(System.Guid)
    };
    foreach (Type type in types)
    {
         try
         {
               return Convert.ChangeType(value, type);
         }
         catch (Exception)
         {
             continue;
         }
    }
    return value;
}

我觉得这种方法可能不是最佳实践,因为它只能匹配预定义的类型。

通常我发现。net以比我的实现更好的方式容纳这个功能,所以我的问题是:有没有更好的方法来解决这个问题和/或这个功能在。net中实现得更好?

EDIT:请注意,数组中类型的排序是为了尽可能准确地出现给定类型的"最佳匹配"。

EDIT:作为miniBill的请求,这是如何使用该方法的(简单的例子!):

JsonDictionary["myKey"] = ConvertToType("255"); // 255 is a stringified json value, which should be assigned to myKey as a byte.

. net中的智能类型转换

你的方法不理想,因为如果value不是SByte,它将导致一系列异常。

鉴于所有这些类型共享一个共同的方法.TryParse(string, out T),我们可以使用反射提取方法并为每个类型调用它。我将该方法作为string上的扩展方法,并将Type[]数组分解为自己的延迟加载属性,以便更快地使用。

public static class StringExtensions
{
    public static dynamic ConvertToType(this string value)
    {
        foreach (Type type in ConvertibleTypes)
        {
            var obj = Activator.CreateInstance(type);
            var methodParameterTypes = new Type[] { typeof(string), type.MakeByRefType() };
            var method = type.GetMethod("TryParse", methodParameterTypes);
            var methodParameters = new object[] { value, obj };
            bool success = (bool)method.Invoke(null, methodParameters);
            if (success)
            {
                return methodParameters[1];
            }
        }
        return value;
    }
    private static Type[] _convertibleTypes = null;
    private static Type[] ConvertibleTypes
    {
        get
        {
            if (_convertibleTypes == null)
            {
                _convertibleTypes = new Type[]
                {
                    typeof(System.SByte),
                    typeof(System.Byte), 
                    typeof(System.Int16), 
                    typeof(System.UInt16), 
                    typeof(System.Int32), 
                    typeof(System.UInt32), 
                    typeof(System.Int64), 
                    typeof(System.UInt64), 
                    typeof(System.Single), 
                    typeof(System.Double), 
                    typeof(System.Decimal),
                    typeof(System.DateTime),
                    typeof(System.Guid)
                };
            }
            return _convertibleTypes;
        }
    }
}

用法:

string value = "2391203921";
dynamic converted = value.ConvertToType();

你的方法会起作用,但是,正如你所说,它不是那么优雅。

我认为你有两种方法来改进这段代码:

  1. 将数组移出函数,如psubsee2003所述
  2. 使用TryParse方法进行更便宜的测试(不涉及捕获)(例如:Int32.TryParse)
  3. 实际上写一个解析器,在修剪之后,
    • 检查号码是否为GUID
      • 在> 0的位置有'-'吗?
      • if(Guid.TryParse)
        • 返回结果
      • 返回字符串(不能是数字!)
    • 检查数字是否为小数(它是否有一个点在里面?)
      • 尝试使用各种TryParse
      • 转换为单、双、十进制
      • 如果失败返回字符串
    • 是否以负号开头?
      • 尝试解析为Int64,然后检查大小,看看它适合哪里(<256 -> ubyte, <65536年ushort…)
      • 如果失败返回字符串
    • 尝试解析为Int64
      • 如果它工作检查最小尺寸它适合
      • 如果失败,它可能是一个整数,但太大,尝试解析为双精度,如果失败返回字符串

这是我之前写的一些东西,可能会有所帮助:

public static Boolean CanCovertTo(this String value, Type type)
{
    var targetType = type.IsNullableType() ? Nullable.GetUnderlyingType(type) : type;
    TypeConverter converter = TypeDescriptor.GetConverter(targetType);
    return converter.IsValid(value);
}

基本思想是,如果你传递字符串和你想要测试的Type,你可以在尝试转换之前检查转换是否有效。

这个设计的问题是TypeConverter.IsValid()只是TypeConverter.CanConvertFrom()的包装器(带有一些异常处理),所以你真的没有消除异常处理,但是因为它是BCL的一部分,我倾向于认为这将是一个更好的实现。

你可以这样实现:

private static Type[] defaultTypes = new Type[]
{
    typeof(System.SByte),
    typeof(System.Byte), 
    typeof(System.Int16), 
    typeof(System.UInt16), 
    typeof(System.Int32), 
    typeof(System.UInt32), 
    typeof(System.Int64), 
    typeof(System.UInt64), 
    typeof(System.Single), 
    typeof(System.Double), 
    typeof(System.Decimal),
    typeof(System.DateTime),
    typeof(System.Guid)
};
public static dynamic ConvertToType(string value)
{
    return ConvertToType(value, defaultTypes);
}
public static dynamic ConvertToType(string value, Type[] types)
{
    foreach (Type type in types)
    {
        if (!value.CanConvertTo(type))
            continue;
        return Convert.ChangeType(value, type);
    }
    return value;
}
如果没有异常处理(甚至是TypeConverter.IsValid方法中的异常处理),没有一个很好的方法可以做到这一点,所以如果你真的需要这样一个方法,你必须忍受它。但是,如果您实现miniBill回答中的一些建议,并对设计进行一些改进,则可以限制对异常处理的需求。

您可以使用Reflection通过调用TryParse方法来处理所有Parse类型,这将比使用ChangeType处理多个异常快一点

public Type[] PredefinedTypes = new Type[]
{
    typeof(System.SByte),
    typeof(System.Byte), 
    typeof(System.Int16), 
    typeof(System.UInt16), 
    typeof(System.Int32), 
    typeof(System.UInt32), 
    typeof(System.Int64), 
    typeof(System.UInt64), 
    typeof(System.Single), 
    typeof(System.Double), 
    typeof(System.Decimal),
    typeof(System.DateTime),
    typeof(System.Guid)
};

public dynamic ConvertToType(string value)
{
    foreach (var predefinedType in PredefinedTypes.Where(t => t.GetMethods().Any(m => m.Name.Equals("TryParse"))))
    {
        var typeInstance = Activator.CreateInstance(predefinedType);
        var methodParamTypes = new Type[] { typeof(string), predefinedType.MakeByRefType() };
        var methodArgs = new object[] { value, typeInstance };
        if ((bool)predefinedType.GetMethod("TryParse", methodParamTypes).Invoke(predefinedType, methodArgs))
        {
            return methodArgs[1];
        }
    }
    return value
}