我想这样做:

myYear = record.GetValueOrNull<int?>("myYear"),

注意将可空类型作为泛型参数。

因为GetValueOrNull函数可以返回null,我的第一次尝试是:

public static T GetValueOrNull<T>(this DbDataRecord reader, string columnName)
  where T : class
{
    object columnValue = reader[columnName];

    if (!(columnValue is DBNull))
    {
        return (T)columnValue;
    }
    return null;
}

但我现在得到的错误是:

类型'int?'必须是引用类型,以便在泛型类型或方法中使用它作为参数'T'

没错!Nullable<int>是一个结构体!所以我尝试将类约束改为结构约束(作为副作用不能再返回null):

public static T GetValueOrNull<T>(this DbDataRecord reader, string columnName)
  where T : struct

现在是作业:

myYear = record.GetValueOrNull<int?>("myYear");

给出以下错误:

类型'int?'必须是一个非空值类型,以便在泛型类型或方法中使用它作为参数'T'

是否可以将可空类型指定为泛型参数?


当前回答

多个通用约束不能以OR方式组合(限制较少),只能以AND方式组合(限制较多)。这意味着一个方法不能同时处理两种情况。泛型约束也不能用于为方法创建唯一的签名,因此必须使用两个单独的方法名。

但是,您可以使用泛型约束来确保正确使用方法。

在我的例子中,我特别希望返回null,而不是任何可能的值类型的默认值。GetValueOrDefault = bad。GetValueOrNull = good。

我使用“Null”和“Nullable”来区分引用类型和值类型。这里是我写的两个扩展方法的例子,它们补充了System.Linq.Enumerable类中的FirstOrDefault方法。

    public static TSource FirstOrNull<TSource>(this IEnumerable<TSource> source)
        where TSource: class
    {
        if (source == null) return null;
        var result = source.FirstOrDefault();   // Default for a class is null
        return result;
    }

    public static TSource? FirstOrNullable<TSource>(this IEnumerable<TSource?> source)
        where TSource : struct
    {
        if (source == null) return null;
        var result = source.FirstOrDefault();   // Default for a nullable is null
        return result;
    }

其他回答

这可能是一个死线程,但我倾向于使用以下方法:

public static T? GetValueOrNull<T>(this DbDataRecord reader, string columnName)
where T : struct 
{
    return reader[columnName] as T?;
}

我知道这很老了,但这里有另一个解决方案:

public static bool GetValueOrDefault<T>(this SqlDataReader Reader, string ColumnName, out T Result)
{
    try
    {
        object ColumnValue = Reader[ColumnName];

        Result = (ColumnValue!=null && ColumnValue != DBNull.Value) ? (T)ColumnValue : default(T);

        return ColumnValue!=null && ColumnValue != DBNull.Value;
    }
    catch
    {
        // Possibly an invalid cast?
        return false;
    }
}

现在,你不关心T是值类型还是引用类型。只有当函数返回true时,您才能从数据库中得到一个合理的值。 用法:

...
decimal Quantity;
if (rdr.GetValueOrDefault<decimal>("YourColumnName", out Quantity))
{
    // Do something with Quantity
}

这种方法与int非常相似。TryParse("123", out MyInt);

多个通用约束不能以OR方式组合(限制较少),只能以AND方式组合(限制较多)。这意味着一个方法不能同时处理两种情况。泛型约束也不能用于为方法创建唯一的签名,因此必须使用两个单独的方法名。

但是,您可以使用泛型约束来确保正确使用方法。

在我的例子中,我特别希望返回null,而不是任何可能的值类型的默认值。GetValueOrDefault = bad。GetValueOrNull = good。

我使用“Null”和“Nullable”来区分引用类型和值类型。这里是我写的两个扩展方法的例子,它们补充了System.Linq.Enumerable类中的FirstOrDefault方法。

    public static TSource FirstOrNull<TSource>(this IEnumerable<TSource> source)
        where TSource: class
    {
        if (source == null) return null;
        var result = source.FirstOrDefault();   // Default for a class is null
        return result;
    }

    public static TSource? FirstOrNullable<TSource>(this IEnumerable<TSource?> source)
        where TSource : struct
    {
        if (source == null) return null;
        var result = source.FirstOrDefault();   // Default for a nullable is null
        return result;
    }

较短的方法:

public static T ValueOrDefault<T>(this DataRow reader, string columnName) => 
        reader.IsNull(columnName) ? default : (T) reader[columnName];

int返回0,int返回null ?

下面是一个我用了很多年的扩展方法:

public static T GetValue<T>(this DbDataReader reader, string columnName)
{
    if (reader == null) throw new ArgumentNullException(nameof(reader));
    if (string.IsNullOrWhiteSpace(columnName))
        throw new ArgumentException("Value cannot be null or whitespace.", nameof(columnName));

    // do not swallow exceptions here - let them bubble up to the calling API to be handled and/or logged
    var index = reader.GetOrdinal(columnName);
    if (!reader.IsDBNull(index))
    {
        return (T)reader.GetValue(index);
    }
    return default;
}