当前位置:  开发笔记 > 编程语言 > 正文

检查DBNull然后分配给变量的最有效方法是什么?

如何解决《检查DBNull然后分配给变量的最有效方法是什么?》经验,为你挑选了9个好方法。

这个问题偶尔出现,但我没有看到一个满意的答案.

典型的模式是(row是DataRow):

 if (row["value"] != DBNull.Value)
 {
      someObject.Member = row["value"];
 }

我的第一个问题是哪个更有效率(我翻了条件):

  row["value"] == DBNull.Value; // Or
  row["value"] is DBNull; // Or
  row["value"].GetType() == typeof(DBNull) // Or... any suggestions?

这表明.GetType()应该更快,但编译器可能知道一些我不知道的技巧?

第二个问题,是否值得缓存row ["value"]的值,或者编译器是否优化了索引器?

例如:

  object valueHolder;
  if (DBNull.Value == (valueHolder = row["value"])) {}

笔记:

    row ["value"]存在.

    我不知道列的列索引(因此列名称查找).

    我具体要求检查DBNull然后分配(不是关于过早优化等).

我对几个场景进行了基准测试(时间以秒为单位,10,000,000次试验):

row["value"] == DBNull.Value: 00:00:01.5478995
row["value"] is DBNull: 00:00:01.6306578
row["value"].GetType() == typeof(DBNull): 00:00:02.0138757

Object.ReferenceEquals与"=="具有相同的性能

最有趣的结果?如果您不匹配逐列的名称(例如,"值"而不是"值",则大约需要十倍的时间(对于字符串):

row["Value"] == DBNull.Value: 00:00:12.2792374

故事的寓意似乎是,如果您无法通过索引查找列,请确保您提供给索引器的列名称与DataColumn的名称完全匹配.

缓存该值似乎也几乎快了两倍:

No Caching: 00:00:03.0996622
With Caching: 00:00:01.5659920

所以最有效的方法似乎是:

 object temp;
 string variable;
 if (DBNull.Value != (temp = row["value"]))
 {
      variable = temp.ToString();
 }

Dan Tao.. 71

我肯定错过了什么.是不是要DBNull确切检查DataRow.IsNull方法的作用?

我一直在使用以下两种扩展方法:

public static T? GetValue(this DataRow row, string columnName) where T : struct
{
    if (row.IsNull(columnName))
        return null;

    return row[columnName] as T?;
}

public static string GetText(this DataRow row, string columnName)
{
    if (row.IsNull(columnName))
        return string.Empty;

    return row[columnName] as string ?? string.Empty;
}

用法:

int? id = row.GetValue("Id");
string name = row.GetText("Name");
double? price = row.GetValue("Price");

如果您不想要Nullable返回值GetValue,则可以轻松返回default(T)或使用其他选项.


在一个不相关的说明中,这是一个VB.NET替代Stevo3000的建议:

oSomeObject.IntMember = If(TryConvert(Of Integer)(oRow("Value")), iDefault)
oSomeObject.StringMember = If(TryCast(oRow("Name"), String), sDefault)

Function TryConvert(Of T As Structure)(ByVal obj As Object) As T?
    If TypeOf obj Is T Then
        Return New T?(DirectCast(obj, T))
    Else
        Return Nothing
    End If
End Function

丹再次冒着OP希望避免的风险.通过编写`row.IsNull(columnName)`你已经读过一次并再次读它.不是说这会有所作为,但理论上它可能效率较低.. (3认同)

是不是`System.Data.DataSetExtensions.DataRowExtensions.Field (这个System.Data.DataRow,string)`做与第一个方法基本相同的事情? (2认同)


Jon Grant.. 34

你应该使用这个方法:

Convert.IsDBNull()

考虑到它是框架内置的,我希望这是最有效的.

我建议采取以下措施:

int? myValue = (Convert.IsDBNull(row["column"]) ? null : (int?) Convert.ToInt32(row["column"]));

是的,编译器应该为您缓存它.



1> Dan Tao..:

我肯定错过了什么.是不是要DBNull确切检查DataRow.IsNull方法的作用?

我一直在使用以下两种扩展方法:

public static T? GetValue(this DataRow row, string columnName) where T : struct
{
    if (row.IsNull(columnName))
        return null;

    return row[columnName] as T?;
}

public static string GetText(this DataRow row, string columnName)
{
    if (row.IsNull(columnName))
        return string.Empty;

    return row[columnName] as string ?? string.Empty;
}

用法:

int? id = row.GetValue("Id");
string name = row.GetText("Name");
double? price = row.GetValue("Price");

如果您不想要Nullable返回值GetValue,则可以轻松返回default(T)或使用其他选项.


在一个不相关的说明中,这是一个VB.NET替代Stevo3000的建议:

oSomeObject.IntMember = If(TryConvert(Of Integer)(oRow("Value")), iDefault)
oSomeObject.StringMember = If(TryCast(oRow("Name"), String), sDefault)

Function TryConvert(Of T As Structure)(ByVal obj As Object) As T?
    If TypeOf obj Is T Then
        Return New T?(DirectCast(obj, T))
    Else
        Return Nothing
    End If
End Function


丹再次冒着OP希望避免的风险.通过编写`row.IsNull(columnName)`你已经读过一次并再次读它.不是说这会有所作为,但理论上它可能效率较低..
是不是`System.Data.DataSetExtensions.DataRowExtensions.Field (这个System.Data.DataRow,string)`做与第一个方法基本相同的事情?

2> Jon Grant..:

你应该使用这个方法:

Convert.IsDBNull()

考虑到它是框架内置的,我希望这是最有效的.

我建议采取以下措施:

int? myValue = (Convert.IsDBNull(row["column"]) ? null : (int?) Convert.ToInt32(row["column"]));

是的,编译器应该为您缓存它.


好吧,*所有*提到的选项都内置在框架中......实际上,Convert.IsDBNull做了很多与IConvertible相关的额外工作......

3> Marc Gravell..:

编译器不会优化索引器(即如果你使用row ["value"]两次),所以是的,它稍微快一点:

object value = row["value"];

然后使用两次值; 使用.GetType()会出现问题,如果它为null ...

DBNull.Value实际上是一个单身,所以要添加第四个选项 - 你也许可以使用ReferenceEquals - 但实际上,我认为你在这里过于担心......我不认为"是","==之间的速度不同"等等将成为您遇到的任何性能问题的原因.描述您的整个代码,并专注于重要的事情......它不会是这个.


几乎在所有情况下,==将等同于ReferenceEquals(特别是DBNull),并且它更具可读性.如果你愿意,可以使用@Marc Gravell的优化,但是我和他在一起 - 可能不会有多大帮助.顺便说一句,引用相等应该始终击败类型检查.

4> stevehipwell..:

我会在C#中使用以下代码(VB.NET并不那么简单).

如果值不是null/DBNull,则代码分配值,否则它将默认值设置为LHS值,允许编译器忽略赋值.

oSomeObject.IntMemeber = oRow["Value"] as int? ?? iDefault;
oSomeObject.StringMember = oRow["Name"] as string ?? sDefault;



5> nawfal..:

我觉得这里只有很少的方法不会让潜在客户OP担心最担心(Marc Gravell,Stevo3000,Richard Szalay,Neil,Darren Koppand)并且大多数都是不必要的复杂.充分意识到这是无用的微优化,让我说你应该基本上采用这些:

1)不要读的DataReader/DataRow的值的两倍-这样无论是null检查之前高速缓存,并施放/转换,甚至更好的直接传递record[X]的对象与适当的签名的自定义扩展方法.

2)要遵守上述规定,不要在IsDBNullDataReader/DataRow上使用内置函数,因为它会在record[X]内部调用,所以实际上你会做两次.

3)作为一般规则,类型比较总是比值比较慢.只要做到record[X] == DBNull.Value更好.

4)直接投射比调用Convert类转换更快,但我担心后者会更少动摇.

5)最后,通过索引而不是列名访问记录将再次更快.


我觉得通过Szalay,Neil和Darren Koppand的方法会更好.我特别喜欢Darren Koppand的扩展方法方法,它接受IDataRecord(虽然我想进一步缩小IDataReader)和索引/列名.

小心打电话给它:

record.GetColumnValue("field");

并不是

record.GetColumnValue("field");

如果你需要区分0DBNull.例如,如果枚举字段中包含空值,否则default(MyEnum)将返回冒第一个枚举值的风险.那么更好的电话record.GetColumnValue("Field").

既然你从读书DataRow,我将创建两个扩展方法DataRow,并IDataReader通过干燥公共代码.

public static T Get(this DataRow dr, int index, T defaultValue = default(T))
{
    return dr[index].Get(defaultValue);
}

static T Get(this object obj, T defaultValue) //Private method on object.. just to use internally.
{
    if (obj.IsNull())
        return defaultValue;

    return (T)obj;
}

public static bool IsNull(this T obj) where T : class 
{
    return (object)obj == null || obj == DBNull.Value;
} 

public static T Get(this IDataReader dr, int index, T defaultValue = default(T))
{
    return dr[index].Get(defaultValue);
}

所以现在称之为:

record.Get(1); //if DBNull should be treated as 0
record.Get(1); //if DBNull should be treated as null
record.Get(1, -1); //if DBNull should be treated as a custom value, say -1

我相信这是应该已经在框架(而不是record.GetInt32,record.GetString等方法)摆在首位-没有运行时异常,使我们能够灵活地处理空值.

根据我的经验,我从一个通用方法中读取数据库的运气不太好.我总是不得不自定义处理各种类型的,所以我不得不写我自己的GetInt,GetEnum,GetGuid等从长远看方法.如果您想在默认情况下从db读取字符串时修剪空格,或者将其DBNull视为空字符串,该怎么办?或者,如果您的小数应该被截断所有尾随零.Guid当底层数据库可以将它们存储为字符串或二进制文件时,不同的连接器驱动程序表现不同的类型我遇到了最大的问题.我有这样的重载:

static T Get(this object obj, T defaultValue, Func converter)
{
    if (obj.IsNull())
        return defaultValue;

    return converter  == null ? (T)obj : converter(obj);
}

使用Stevo3000的方法,我发现调用有点丑陋和繁琐,并且很难用它来制作泛型函数.



6> 小智..:

有一个麻烦的情况,对象可能是一个字符串.以下扩展方法代码处理所有情况.以下是您将如何使用它:

    static void Main(string[] args)
    {
        object number = DBNull.Value;

        int newNumber = number.SafeDBNull();

        Console.WriteLine(newNumber);
    }



    public static T SafeDBNull(this object value, T defaultValue) 
    {
        if (value == null)
            return default(T);

        if (value is string)
            return (T) Convert.ChangeType(value, typeof(T));

        return (value == DBNull.Value) ? defaultValue : (T)value;
    } 

    public static T SafeDBNull(this object value) 
    { 
        return value.SafeDBNull(default(T)); 
    } 



7> Dylan Beatti..:

我个人赞成这种语法,它使用公开的显式IsDbNull方法IDataRecord,并缓存列索引以避免重复的字符串查找.

为了便于阅读,它扩展如下:

int columnIndex = row.GetOrdinal("Foo");
string foo; // the variable we're assigning based on the column value.
if (row.IsDBNull(columnIndex)) {
  foo = String.Empty; // or whatever
} else { 
  foo = row.GetString(columnIndex);
}

重写为适合DAL代码中的紧凑性的单行 - 请注意,在此示例中,我们将分配int bar = -1if row["Bar"]为null.

int i; // can be reused for every field.
string foo  = (row.IsDBNull(i  = row.GetOrdinal("Foo")) ? null : row.GetString(i));
int bar = (row.IsDbNull(i = row.GetOrdinal("Bar")) ? -1 : row.GetInt32(i));

如果您不知道内联分配可能会令人困惑,但它会将整个操作保留在一行上,我认为当您从一个代码块中的多个列填充属性时,可以提高可读性.


DataRow虽然没有实现IDataRecord.

8> Richard Szal..:

不是我已经完成了这个,但你可以绕过双索引器调用并仍然使用静态/扩展方法保持代码清洁.

IE浏览器.

public static IsDBNull(this object value, T default)
{
    return (value == DBNull.Value)
        ? default
        : (T)value;
}

public static IsDBNull(this object value)
{
    return value.IsDBNull(default(T));
}

然后:

IDataRecord record; // Comes from somewhere

entity.StringProperty = record["StringProperty"].IsDBNull(null);
entity.Int32Property = record["Int32Property"].IsDBNull(50);

entity.NoDefaultString = record["NoDefaultString"].IsDBNull();
entity.NoDefaultInt = record["NoDefaultInt"].IsDBNull();

还具有将空检查逻辑保持在一个地方的好处.当然,下行是一种额外的方法调用.

只是一个想法.


但是,在对象上添加扩展方法非常广泛.就个人而言,我可能已经考虑过DataRow上的扩展方法,但不是对象.

9> bdukes..:

我尝试尽可能避免这种检查。

显然,不必为不能容纳的列进行处理null

如果要存储为Nullable值类型(int?等),则可以使用进行转换as int?

如果您不需要区分string.Emptynull,您可以致电.ToString(),因为DBNull将返回string.Empty

推荐阅读
yzh148448
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有