这个问题偶尔出现,但我没有看到一个满意的答案.
典型的模式是(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
Jon Grant.. 34
你应该使用这个方法:
Convert.IsDBNull()
考虑到它是框架内置的,我希望这是最有效的.
我建议采取以下措施:
int? myValue = (Convert.IsDBNull(row["column"]) ? null : (int?) Convert.ToInt32(row["column"]));
是的,编译器应该为您缓存它.
我肯定错过了什么.是不是要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
你应该使用这个方法:
Convert.IsDBNull()
考虑到它是框架内置的,我希望这是最有效的.
我建议采取以下措施:
int? myValue = (Convert.IsDBNull(row["column"]) ? null : (int?) Convert.ToInt32(row["column"]));
是的,编译器应该为您缓存它.
编译器不会优化索引器(即如果你使用row ["value"]两次),所以是的,它稍微快一点:
object value = row["value"];
然后使用两次值; 使用.GetType()会出现问题,如果它为null ...
DBNull.Value
实际上是一个单身,所以要添加第四个选项 - 你也许可以使用ReferenceEquals - 但实际上,我认为你在这里过于担心......我不认为"是","==之间的速度不同"等等将成为您遇到的任何性能问题的原因.描述您的整个代码,并专注于重要的事情......它不会是这个.
我会在C#中使用以下代码(VB.NET并不那么简单).
如果值不是null/DBNull,则代码分配值,否则它将默认值设置为LHS值,允许编译器忽略赋值.
oSomeObject.IntMemeber = oRow["Value"] as int? ?? iDefault; oSomeObject.StringMember = oRow["Name"] as string ?? sDefault;
我觉得这里只有很少的方法不会让潜在客户OP担心最担心(Marc Gravell,Stevo3000,Richard Szalay,Neil,Darren Koppand)并且大多数都是不必要的复杂.充分意识到这是无用的微优化,让我说你应该基本上采用这些:
1)不要读的DataReader/DataRow的值的两倍-这样无论是null检查之前高速缓存,并施放/转换,甚至更好的直接传递record[X]
的对象与适当的签名的自定义扩展方法.
2)要遵守上述规定,不要在IsDBNull
DataReader/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");
如果你需要区分0
和DBNull
.例如,如果枚举字段中包含空值,否则default(MyEnum)
将返回冒第一个枚举值的风险.那么更好的电话record.GetColumnValue
.
既然你从读书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
使用Stevo3000的方法,我发现调用有点丑陋和繁琐,并且很难用它来制作泛型函数.
有一个麻烦的情况,对象可能是一个字符串.以下扩展方法代码处理所有情况.以下是您将如何使用它:
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)); }
我个人赞成这种语法,它使用公开的显式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 = -1
if 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));
如果您不知道内联分配可能会令人困惑,但它会将整个操作保留在一行上,我认为当您从一个代码块中的多个列填充属性时,可以提高可读性.
不是我已经完成了这个,但你可以绕过双索引器调用并仍然使用静态/扩展方法保持代码清洁.
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 ();
还具有将空检查逻辑保持在一个地方的好处.当然,下行是一种额外的方法调用.
只是一个想法.
我尝试尽可能避免这种检查。
显然,不必为不能容纳的列进行处理null
。
如果要存储为Nullable值类型(int?
等),则可以使用进行转换as int?
。
如果您不需要区分string.Empty
和null
,您可以致电.ToString()
,因为DBNull将返回string.Empty
。