如何编写将 DBNull 值转换为可为空的泛型方法?



我有一个foo_date数据库,其中Npgsql将sqlNULL值映射到C#类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"]);

但是,我收到编译器错误:

错误 CS0403 无法将 null 转换为类型参数"T",因为它可能是不可为空的值类型。考虑改用"默认(T)"。

当我用return default(T?)替换return null时,编译器很高兴,但该方法不返回null而是返回默认Date,即01.01.0001

编写上述通用实用程序方法的正确方法是什么?

我假设您启用了可为空的引用类型,否则T?将是编译器错误。

不幸的是,T?,其中T是泛型类型参数,具有多种含义。

  • T是值类型(即您有一个where T : struct约束)时,T?表示Nullable<T>
  • T是引用类型(即您有一个where T : class约束)时,T?表示允许为 null 的引用类型。
  • T不受约束时,T?表示"如果T是引用类型,则允许为 null;否则无效"。

换句话说,如果您有:

T? Foo<T>() => default;

如果你打电话给Foo<int>(),你会得到一个int,而不是一个int?

但是,如果您有:

T? Foo<T>() where T : struct => default;

然后Foo<int>()返回一个int?

换句话说,您的签名需要:

public static T? CastToNullable<T>(object obj) where T : struct
{
if (DBNull.Value.Equals(obj))
return null;
return (T)obj;
}

请检查可为空的结构,它实际上是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;
}

似乎您想访问 NpgsqlDataReader 上的列,但要为空列获取 .NET null 而不是DBNull.Value.如果是这样,通常的方法是编写使用扩展方法,如下所示:

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);
}

这可以直接在您的阅读器上使用:

var s = reader.GetValueOrDefault<string>(0);

最新更新