【问题标题】:Is TryParse a bad idea when using a DataReader?使用 DataReader 时 TryParse 是不是一个坏主意?
【发布时间】:2014-03-21 21:49:22
【问题描述】:

所以,我一直在阅读一些代码,看到了一个有趣的模式,用于解析来自数据阅读器的数据。它看起来像:

public static long ParseLong(IDataReader reader, string field, long default)
{
    long result;
    var value = GetValue(reader, field);
    if (value == DBNull.Value) return default;

    // is this next line bad?
    if (Int64.TryParse(value.ToString(), out result)) return result;
    return default;
}

这种模式不适合我,因为它隐藏了类型转换异常,并且似乎转换为字符串然后再次转换比仅调用“Convert.ToInt64(value)”要慢。

我在这方面是不是有点离谱?是否有更好的模式来解析来自数据读取器的数据?

注意 - 在我的例子中,列的类型是“NUMBER(20)”。即使不是这种情况,最好还是抛出异常,以便开发人员在投入生产之前解决问题,对吧?

【问题讨论】:

  • 你对解析有什么要求?跳过格式错误的数据是否有效?
  • 这些行似乎更像是对设计不良的数据表的修复,其中长值存储在字符串字段中,然后需要准确解析以避免错误。如果字段被正确定义为 Long 则无需 TryParse 它们,只需转换即可(当然 null 检查仍然有效)
  • 您是否将数字数据存储在字符字段中?如果不是,那么 value 如果不为 null,则应该已经是 long。
  • 实际上这个特定的代码库使用内联sql(参数化)。因此,代码中断的唯一原因是列的类型发生了变化或内联 sql 发生了变化。
  • IDataReader 有一种方法可以将值读取为 long IDataRecord.GetInt64 为什么不直接使用它?您的方法导致将 long 装箱到对象,然后将其拆箱以再次创建 long 。可以使用IDataRecord.IsDBNull方法来检查值是否为null。

标签: c# casting datareader


【解决方案1】:

是的,这很糟糕。

如果列已经是sql数字类型之一,这样做不仅速度慢,而且如果机器的数字格式使用了一些奇怪的分隔符,也会引入bug。如果您知道该列已经是一个数字,则应该将其转换为 long

另一方面,此方法没有可用的上下文。从方法的角度来看,该列可以是任何东西。事实上,找到由想要对所有内容使用字符串类型(nvarchar 等)的人设计的数据库是相当普遍的,无论这可能是错误的。这段代码确实有一个小的优势,如果列使用字符串类型来保存数字数据,它仍然可以工作,并且允许一个想要真正通用的方法使用这种技术是公平的(尽管我个人可能想先尝试转换,然后只有在转换失败时才回退到字符串技术)。

但是,此代码中还有另一个问题:DBNull 检查。 如果您要转换为字符串并对其进行解析,则空值检查是多余的。 DBNull.Value.ToString() 返回一个空字符串,这将使 TryParse() 调用失败并导致返回默认的 long,与检查相同。只是不需要代码。

因此,我们现在处于这样一种情况,要么是因为字符串转换而出现问题,要么是如果您允许字符串转换,因为空值检查而出现问题。无论哪种方式都失败了。我接受此代码的唯一原因是,如果您可以证明您确实需要字符串转换,并且在分析之后您发现程序花费大量时间不必要地将 DBNull 转换为字符串。然后添加更快的空检查就可以了。

根据您的编辑,我确实有一点要为这段代码辩护。对docs for the long type 的快速检查表明,long 的最大值为 19 位。您最多可以存储 20 个。因此,此代码 可能 用于防止 9,223,372,036,854,775,807 和 99,999,999,999,999,999,999 之间的非常大的值适合列但不适合长类型。 .. 即使在这种情况下,我的偏好是为这些值抛出异常,而不是仅仅返回 0。

最后一个想法。此代码有效,因为IDataReader 实现了IDataRecord 接口。该方法应该只要求一个IDataRecord,以使其更灵活。

【讨论】:

  • 我很欣赏深思熟虑的答案。你采取了与我的想法类似的方向,但提出了许多其他非常好的观点。谢谢!
【解决方案2】:

没有理由通过字符串解析值,因为它可以隐藏数据错误。 您可以使用方法的泛型变体:

public static T Parse<T>(IDataReader reader, string field, T default)
{
    int columnIndex = reader.GetOrdinal(field);
    if (reader.IsDBNull(columnIndex)) return default;    
    return (T)reader.GetValue(columnIndex);
}

IDataRecord 接口为您提供类型特定的读取,而不是使用IDataRecord.GetInt64 方法读取,但您还必须检查 DBNull。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-10-21
    • 2012-04-08
    • 1970-01-01
    • 2019-02-01
    • 2011-02-03
    • 2010-11-29
    • 2011-11-13
    • 1970-01-01
    相关资源
    最近更新 更多