首页 文章

Nullable类型作为通用参数可能吗?

提问于
浏览
240

我想做这样的事情:

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>struct !所以我尝试将类约束更改为 struct 约束(并且副作用不能再返回 null ):

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

现在任务:

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

给出以下错误:

类型'int?'必须是非可空值类型才能在泛型类型或方法中将其用作参数“T”

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

10 回答

  • 220

    将返回类型更改为Nullable,并使用非可空参数调用该方法

    static void Main(string[] args)
    {
        int? i = GetValueOrNull<int>(null, string.Empty);
    }
    
    
    public static Nullable<T> GetValueOrNull<T>(DbDataRecord reader, string columnName) where T : struct
    {
        object columnValue = reader[columnName];
    
        if (!(columnValue is DBNull))
            return (T)columnValue;
    
        return null;
    }
    
  • 48
    public static T GetValueOrDefault<T>(this IDataRecord rdr, int index)
    {
        object val = rdr[index];
    
        if (!(val is DBNull))
            return (T)val;
    
        return default(T);
    }
    

    只需使用它:

    decimal? Quantity = rdr.GetValueOrDefault<decimal?>(1);
    string Unit = rdr.GetValueOrDefault<string>(2);
    
  • 2

    只需对原始代码执行两项操作 - 删除 where 约束,并将最后一个 returnreturn null 更改为 return default(T) . 这样你就可以返回你想要的任何类型 .

    顺便说一句,您可以通过将 if 语句更改为 if (columnValue != DBNull.Value) 来避免使用 is .

  • 1

    只需做一些与此类似的令人难以置信的事情 . 我的代码:

    public T IsNull<T>(this object value, T nullAlterative)
    {
        if(value != DBNull.Value)
        {
            Type type = typeof(T);
            if (type.IsGenericType && 
                type.GetGenericTypeDefinition() == typeof(Nullable<>).GetGenericTypeDefinition())
            {
                type = Nullable.GetUnderlyingType(type);
            }
    
            return (T)(type.IsEnum ? Enum.ToObject(type, Convert.ToInt32(value)) :
                Convert.ChangeType(value, type));
        }
        else 
            return nullAlternative;
    }
    
  • 4

    Disclaimer: 此答案有效,但仅用于教育目的 . :)詹姆斯琼斯' solution is probably the best here and certainly the one I'去吧 .

    如果不太安全,C#4.0的 dynamic 关键字会让这更容易:

    public static dynamic GetNullableValue(this IDataRecord record, string columnName)
    {
      var val = reader[columnName];
    
      return (val == DBNull.Value ? null : val);
    }
    

    现在,您不需要在RHS上显式类型提示:

    int? value = myDataReader.GetNullableValue("MyColumnName");
    

    事实上,你根本不需要它!

    var value = myDataReader.GetNullableValue("MyColumnName");
    

    value 现在将是一个int,或一个字符串,或从DB返回的任何类型 .

    唯一的问题是,这并不妨碍您在LHS上使用不可为空的类型,在这种情况下,您将获得一个相当讨厌的运行时异常,如:

    Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Cannot convert null to 'int' because it is a non-nullable value type
    

    与使用 dynamic 的所有代码一样:告诫编码器 .

  • 1

    我想你想要处理引用类型和结构类型 . 我用它将XML Element字符串转换为更类型的字符串 . 您可以使用反射删除nullAlternative . formatprovider用于处理依赖于文化的' . '或','分隔符,例如小数或整数和双精度数 . 这可能有效:

    public T GetValueOrNull<T>(string strElementNameToSearchFor, IFormatProvider provider = null ) 
        {
            IFormatProvider theProvider = provider == null ? Provider : provider;
            XElement elm = GetUniqueXElement(strElementNameToSearchFor);
    
            if (elm == null)
            {
                object o =  Activator.CreateInstance(typeof(T));
                return (T)o; 
            }
            else
            {
                try
                {
                    Type type = typeof(T);
                    if (type.IsGenericType &&
                    type.GetGenericTypeDefinition() == typeof(Nullable<>).GetGenericTypeDefinition())
                    {
                        type = Nullable.GetUnderlyingType(type);
                    }
                    return (T)Convert.ChangeType(elm.Value, type, theProvider); 
                }
                catch (Exception)
                {
                    object o = Activator.CreateInstance(typeof(T));
                    return (T)o; 
                }
            }
        }
    

    你可以像这样使用它:

    iRes = helper.GetValueOrNull<int?>("top_overrun_length");
    Assert.AreEqual(100, iRes);
    
    
    
    decimal? dRes = helper.GetValueOrNull<decimal?>("top_overrun_bend_degrees");
    Assert.AreEqual(new Decimal(10.1), dRes);
    
    String strRes = helper.GetValueOrNull<String>("top_overrun_bend_degrees");
    Assert.AreEqual("10.1", strRes);
    
  • 4

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

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

    我自己也遇到了同样的问题 .

    ... = reader["myYear"] as int?; 工作,很干净 .

    它适用于任何类型,没有问题 . 如果结果是DBNull,则在转换失败时返回null .

  • 97

    我知道这是旧的,但这是另一种解决方案:

    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); 非常相似

  • 3

    多个通用约束不能以OR方式组合(限制性较小),仅以AND方式(更具限制性)组合 . 意味着一种方法无法处理这两种情况 . 通用约束也不能用于为方法创建唯一的签名,因此您必须使用2个单独的方法名称 .

    但是,您可以使用通用约束来确保正确使用方法 .

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

    我使用“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;
        }
    

相关问题