如何编写将 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);