简体   繁体   中英

How to write generic method for casting DBNull value to Nullable?

I have a database with a nullable column foo_date , where Npgsql maps the sql NULL value to an instance of the C# class DBNull . In my C# aggregate I use the type DateTime? for said column. So the question is how to easily convert DBNull to a nullable type.

I want to write a utility method like, eg,

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

which I would like to use like this:

IDataRecord rec = ...
DateTime? fooDate = DbUtil.CastToNullable<DateTime>(rec["foo_date"]);

However, I get the compiler error:

Error CS0403 Cannot convert null to type parameter 'T' because it could be a non-nullable value type. Consider using 'default(T)' instead.

When I replace return null by return default(T?) , the compiler is happy, but the method does not return null but the default Date , ie, 01.01.0001 .

What is the correct way to write the generic utility method above?

Please check Nullable structure which is in fact long/real version of T?

In that case your method would be

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

That would work only for structures, for reference types you could add

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

I'm assuming you've got nullable reference types enabled, otherwise that T? would be a compiler error.

T? , where T is a generic type parameter, has multiple meanings unfortunately.

  • When T is a value type (ie you have a where T: struct constraint), T? means Nullable<T> .
  • When T is a reference type (ie you have a where T: class constraint), T? means a reference type which is allowed to be null.
  • When T is unconstrained, T? means "If T is a reference type, then this is allowed to be null; otherwise no effect".

In other words, if you have:

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

If you call Foo<int>() , you get back an int , not an int? .

If however you have:

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

then Foo<int>() returns an int? .

In other words, your signature needs to be:

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

It seems like you want to access columns on your NpgsqlDataReader, but to get .NET null for null columns instead of DBNull.Value . If so, the usual way is to write use an extension method as follows:

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

This can be used directly on your reader:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM