【问题标题】:Possible to use ?? (the coalesce operator) with DBNull?可以用吗?? (合并运算符)与 DBNull?
【发布时间】:2012-03-15 06:19:13
【问题描述】:

如果我有类似以下的代码:

while(myDataReader.Read())
{
  myObject.intVal = Convert.ToInt32(myDataReader["mycolumn"] ?? 0);
}

它抛出错误:

对象不能从 DBNull 转换为其他类型。

intVal 定义为可为空的int 不是一种选择。我有办法做到以上几点吗?

【问题讨论】:

标签: c# .net null-coalescing-operator


【解决方案1】:

这里还有一个选项:

while (myDataReader.Read())
{
    myObject.intVal = (myDataReader["mycolumn"] as int? ?? 0);
}

【讨论】:

  • 绝对最好的方法!谢谢 Edyn。
  • 绝妙的答案!好干净。
【解决方案2】:

你可以使用扩展方法吗? (写在我的头上)

public static class DataReaderExtensions 
{
    public static T Read<T>(this SqlDataReader reader, string column, T defaultValue = default(T))
    {
        var value = reader[column];

        return (T)((DBNull.Value.Equals(value))
                   ? defaultValue
                   : Convert.ChangeType(value, typeof(T)));
    }
}

你会像这样使用它:

while(myDataReader.Read())
{
  int i = myDataReader.Read<int>("mycolumn", 0);
}

【讨论】:

  • 我最终选择了扩展方法路线。 Convert.ChangeType... 部分出现错误,因此我做了一些小改动。
  • 你能用你必须做的“小改动”来更新我的答案吗?
  • 实际上,我仍然无法让演员正常工作。 Convert.ChangeType (reader[column], typeof(T)) 无法编译,所以我切换到这个(T)reader[column],它会引发运行时异常。有什么想法吗?
  • 做到了,感谢您的跟进。最终回滚了我的小改动
【解决方案3】:

你能简单地使用Int32.Tryparse吗?

int number;
bool result = Int32.TryParse(myDataReader["mycolumn"].ToString(), out number);

根据MSDN,如果转换失败,number将包含0

【讨论】:

  • +1,很好的答案。这本来可以,但我实际上试图分配的 int 是一个对象属性,它不适用于out
  • 这不会编译,是吗?数据读取器索引器返回一个对象引用,TryParse 采用一个字符串参数。当然,你可以在对象上调用ToString,但是仅仅为了解析它而构造一个int的字符串表示是相当低效的。拆箱转换会更有效。
  • @phoog,好点子。如果它为空 ToString 无论如何都会爆炸。可以改用as string
  • @AbeMiessler as string 如果对象是装箱的 int 则返回 null,从而在 TryParse 中引发异常。但是DataReader的索引器保证不返回null;空数据值由DBNull.Value 表示(这将我们带回到我们开始的地方!)。所以如果你打电话给.ToString(),逻辑是合理的;只是效率低下。
  • 我添加了 .ToString() 来编译答案。我当然承认这不是一个有效的解决方案,但感谢您的反馈!
【解决方案4】:

怎么样:

object x = DBNull.Value;
int y = (x as Int32?).GetValueOrDefault(); //This will be 0

或者在你的情况下:

int i = (myDataReader["mycolumn"] as Int32?).GetValueOrDefault();

【讨论】:

  • 正如 John Saunders 在对链接的“可能重复”问题的回答中指出的那样,这种方法的缺点是如果列的数据类型发生更改,则无法引发异常。例如,如果“mycolumn”的short 值为42,则变量i 的值为0。直接强制转换会引发异常,提醒注意代码和数据库之间的类型不匹配。
  • @phoog - 有效点,我想每种方法都有其优点和缺点。就个人而言,我喜欢我的数据库和代码尽可能地紧密耦合。我要么在代码中使用int?,要么不允许在数据库中使用null
【解决方案5】:

为什么不使用空合并运算符 (DBNull.Value != null) 以外的东西:

int i = myDataReader["mycolumn"] == DBNull.Value ?
            Convert.ToInt32(myDataReader["mycolumn"]) :
            0;

你总是可以用一个简洁的扩展方法来包装它:

public static T Read<T>(this DataReader reader, string column, T defaultVal)
{
    if(reader[column] == DBNull.Value) return defaultVal;
    return Convert.ChangeType(reader[column], typeof(T));
}

【讨论】:

  • 我认为是三元 if 但?? 似乎更干净,如果我能做到的话。
  • @AbeMiessler 我不认为?? 将与DBNull 一起使用;实际上不是null
  • 反之亦然。另外我建议这种方法效率不高。
  • 你是不是把我上面贴的扩展方法转储了?
  • @insta - 不。刚刚意识到有一个扩展方法会很好。不过,编写该代码的方法只有这么多(并且在查看之后,代码甚至都不匹配)。
【解决方案6】:

不,仅适用于空值。

对象上的扩展方法如何检查 DBNull 并返回默认值?

//may not compile or be syntactically correct! Just the general idea.
public static object DefaultIfDBNull( this object TheObject, object DefaultValue )
{
    if( TheObject is DBNull )
        return DefaultValue;
    return TheObject;
}

【讨论】:

  • 为什么不使用泛型而不是object
  • @dlev 在处理非通用数据读取器的上下文中,我认为这会更容易。它就在我的脑海中。来自 insta 的通用答案看起来相当不错。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-12-05
  • 1970-01-01
  • 2015-03-27
  • 1970-01-01
  • 2012-11-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多