如何编写将 DBNull 值转换为 Nullable 的通用方法?
How to write generic method for casting DBNull value to Nullable?
我有一个包含可为空列 foo_date
的数据库,其中 Npgsql 将 sql NULL
值映射到 C# class 的实例DBNull
。在我的 C# 聚合中,我对所述列使用类型 DateTime?
。所以问题是如何轻松地将 DBNull
转换为可空类型。
我想编写一个实用方法,例如,
public static class DbUtil
{
public static T? CastToNullable<T>(object obj)
{
if (DBNull.Value.Equals(obj))
return null;
return (T)obj;
}
}
我想这样使用:
IDataRecord rec = ...
DateTime? fooDate = DbUtil.CastToNullable<DateTime>(rec["foo_date"]);
但是,我得到编译器错误:
Error CS0403 Cannot convert null to type parameter 'T' because it could be a non-nullable value type. Consider using 'default(T)' instead.
当我用 return default(T?)
替换 return null
时,编译器很高兴,但该方法不是 return null
,而是默认的 Date
,即, 01.01.0001
.
上面通用实用方法的正确编写方法是什么?
请检查 Nullable 结构,它实际上是 T?
的 long/real 版本
在那种情况下,您的方法将是
public static T? CastToNullable<T>(object obj) where T: struct
{
if (DBNull.Value.Equals(obj))
return null;
return new Nullable<T>((T)obj);
}
这仅适用于结构,对于您可以添加的引用类型
public static T CastToNullableObj<T>(object obj) where T: class
{
if (DBNull.Value.Equals(obj))
return null;
return (T)obj;
}
我假设您已启用可为空的引用类型,否则 T?
将是编译器错误。
T?
,其中T
是泛型类型参数,不幸的是有多重含义。
- 当
T
是值类型时(即你有一个 where T : struct
约束),T?
表示 Nullable<T>
。
- 当
T
是引用类型时(即你有where T : class
约束),T?
表示允许为空的引用类型。
- 当
T
不受约束时,T?
表示“如果T
是引用类型,则允许为null;否则无效”。
换句话说,如果你有:
T? Foo<T>() => default;
如果你调用 Foo<int>()
,你会得到一个 int
,而不是 int?
。
如果您有:
T? Foo<T>() where T : struct => default;
然后 Foo<int>()
returns 一个 int?
.
换句话说,您的签名需要是:
public static T? CastToNullable<T>(object obj) where T : struct
{
if (DBNull.Value.Equals(obj))
return null;
return (T)obj;
}
您似乎想要访问 NpgsqlDataReader 上的列,但要为空列而不是 DBNull.Value
获取 .NET 空值。如果是这样,通常的方法是使用扩展方法编写如下:
public static class DbDataReaderExtensions
{
public static T? GetValueOrDefault<T>(this DbDataReader reader, int ordinal)
where T : class
=> reader.IsDBNull(ordinal) ? null : reader.GetFieldValue<T>(ordinal);
}
这个可以直接用在你的reader:
var s = reader.GetValueOrDefault<string>(0);
我有一个包含可为空列 foo_date
的数据库,其中 Npgsql 将 sql NULL
值映射到 C# class 的实例DBNull
。在我的 C# 聚合中,我对所述列使用类型 DateTime?
。所以问题是如何轻松地将 DBNull
转换为可空类型。
我想编写一个实用方法,例如,
public static class DbUtil
{
public static T? CastToNullable<T>(object obj)
{
if (DBNull.Value.Equals(obj))
return null;
return (T)obj;
}
}
我想这样使用:
IDataRecord rec = ...
DateTime? fooDate = DbUtil.CastToNullable<DateTime>(rec["foo_date"]);
但是,我得到编译器错误:
Error CS0403 Cannot convert null to type parameter 'T' because it could be a non-nullable value type. Consider using 'default(T)' instead.
当我用 return default(T?)
替换 return null
时,编译器很高兴,但该方法不是 return null
,而是默认的 Date
,即, 01.01.0001
.
上面通用实用方法的正确编写方法是什么?
请检查 Nullable 结构,它实际上是 T?
在那种情况下,您的方法将是
public static T? CastToNullable<T>(object obj) where T: struct
{
if (DBNull.Value.Equals(obj))
return null;
return new Nullable<T>((T)obj);
}
这仅适用于结构,对于您可以添加的引用类型
public static T CastToNullableObj<T>(object obj) where T: class
{
if (DBNull.Value.Equals(obj))
return null;
return (T)obj;
}
我假设您已启用可为空的引用类型,否则 T?
将是编译器错误。
T?
,其中T
是泛型类型参数,不幸的是有多重含义。
- 当
T
是值类型时(即你有一个where T : struct
约束),T?
表示Nullable<T>
。 - 当
T
是引用类型时(即你有where T : class
约束),T?
表示允许为空的引用类型。 - 当
T
不受约束时,T?
表示“如果T
是引用类型,则允许为null;否则无效”。
换句话说,如果你有:
T? Foo<T>() => default;
如果你调用 Foo<int>()
,你会得到一个 int
,而不是 int?
。
如果您有:
T? Foo<T>() where T : struct => default;
然后 Foo<int>()
returns 一个 int?
.
换句话说,您的签名需要是:
public static T? CastToNullable<T>(object obj) where T : struct
{
if (DBNull.Value.Equals(obj))
return null;
return (T)obj;
}
您似乎想要访问 NpgsqlDataReader 上的列,但要为空列而不是 DBNull.Value
获取 .NET 空值。如果是这样,通常的方法是使用扩展方法编写如下:
public static class DbDataReaderExtensions
{
public static T? GetValueOrDefault<T>(this DbDataReader reader, int ordinal)
where T : class
=> reader.IsDBNull(ordinal) ? null : reader.GetFieldValue<T>(ordinal);
}
这个可以直接用在你的reader:
var s = reader.GetValueOrDefault<string>(0);