【问题标题】:Generic Type Converter - TypeConverter or Convert.ChangeType通用类型转换器 - TypeConverter 或 Convert.ChangeType
【发布时间】:2018-12-10 23:22:57
【问题描述】:

我正在尝试将 String 转换为泛型类型。泛型类型将是 Int32、Int64、Boolean、Double 等……我尝试了两种方法:

public static Boolean TryParse<T>(String source, out T value) {

  TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));

  try {

    value = (T)converter.ConvertFromString(source);
    return true;

  } catch {

    value = default(T);
    return false;

  }

}

public static Boolean TryChangeType<T>(Object source, out T value) {

  try {

    Type type = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T);

    value = (T)Convert.ChangeType(source, type);
    return true;

  } catch {

    value = default(T);
    return false;

  }

}

第二个更通用,因为它接受一个对象。

我也在考虑在 TryChangeType 中传递一个 IFormatProvider,它将在 Convert.ChangeType 中用于解决文化问题等等。

您认为第二种方法更好吗?

有什么方法可以改进我的代码?

【问题讨论】:

标签: c#


【解决方案1】:

在您的第一个示例中,您可以通过预先调用 CanConvertTo()CanConvertFrom() 来摆脱 try catch 块。

public static bool TryParse<T>(string source, out T value)
{
    TypeConverter converter = TypeDescriptor.GetConverter(typeof (T));
    if (converter.CanConvertTo(typeof (T)) && converter.CanConvertFrom(typeof (string)))
    {
        value = (T)converter.ConvertFromString(source);
        return true;
    }
    else
    {
        value = default (T);
        return false;
    }
}

在第二个例子中,为什么不让它更通用并传入一个通用类型呢?

Convert 仅在类型实现 IConvertible 接口时才有效,因此您可以检查它,另一方面它仍不能确保转换是可能的。

        public static bool TryChangeType<T, TR>(T input, out TR output) where T : IConvertible
    {
        bool result = false;
        try
        {
            Type type = Nullable.GetUnderlyingType(typeof(TR));
            output = (TR)Convert.ChangeType(input, type);
            result = true;
        }
        catch(Exception)
        {
            output = default(TR);
        }
        return result;
    }

最好只捕获你知道的异常:

            catch(InvalidCastException)
        {
            output = default(TR);
            //Conversion is not unsupported
        }
            catch(FormatException)
        {
            output = default(TR);
            //string input value was in incorrect format
        }
            catch(InvalidCastException)
        {
            output = default(TR);
            //Conversion is not unsupported
        }
            catch(OverflowException)
        {
            output = default(TR);
            //narrowing conversion between two numeric types results in loss of data
        }

这可能无法完全回答问题,但您要求可能的改进,所以我想为什么不呢。

【讨论】:

  • 是的,这样就更通用了。好建议。
  • 我强烈建议避免使用许多完全相同的 catch 块,考虑改用 when
  • 另外关于“只捕获你知道的异常”,IMO这是一个判断调用,如果你使用catch(Exception)你假设如果框架类抛出异常,它将是可恢复的;如果您只捕获已知异常,则假设整个框架已经不可恢复地自行拆除(但没有使用CSE exception type)。我认为前者更有可能和I have ran into it in the past.
  • ... 基本上没有捕获所有异常是在说“如果框架的文档缺少任何方式(编译器不强制执行抛出的异常),或者框架中存在任何错误,我的应用程序应该崩溃”,而捕获所有异常是在说“如果框架以意外的方式失败,没有标记为损坏状态,我负责回滚任何更改,因为这个块中发生的任何事情未定义”。诚然,这种“回滚”的处理非常重要,必须小心谨慎。
【解决方案2】:

第二个仅适用于IConvertible 类型。如果这是您想要的,您可能也需要应用约束(ChangeType 无论如何都会为不可转换的类型抛出异常):

public static Boolean TryChangeType<T>(Object source, out T value)
    where T : IConvertible
{
    // ...
}

第一个比较通用,TypeConverter在应该使用.NET组件模型的时候使用。例如,在设计器中,类型转换器用于转换属性网格中字符串的值。但是你也应该在这里添加一个小的额外检查:

if (!converter.CanConvertFrom(typeof(string)))
    return false;

此外,我要提一下,如果您不想在不同的区域设置(例如浮点值的情况下)遇到麻烦,您应该使用 ConvertFromInvariantString 方法...

【讨论】:

  • 关于 ConvertFromInvariantString 这是一个很好的提示......我在用“.”转换双精度时遇到了问题。或“,”...但是使用第二个选项我可以传递一个 IFormatProvider ...这解决了第二个示例中的问题,对吗?
  • 是的,使用指定的文化也可以解决问题。顺便说一句,您也可以在此处从任何 object 源进行转换。只需使用CanConvertFrom(Type) 方法。
【解决方案3】:

在您的第一个示例中,TypeDescriptor.GetConverter(Type) 可以抛出异常,因此将其移到 try 块中。在没有亲自尝试的情况下,在您的两种方法中,我喜欢第二种方法。

This post 展示了一个不使用 try/catch 并声称性能优势的可转换性测试示例。

【讨论】:

  • 有趣...摆脱 Try Catch 块是我想做的事情...不知道这是可能的。
猜你喜欢
  • 2013-12-21
  • 1970-01-01
  • 2011-07-21
  • 1970-01-01
  • 2021-01-11
  • 1970-01-01
  • 1970-01-01
  • 2015-09-02
相关资源
最近更新 更多