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

时间:2008-10-16 15:53:43

标签: c# generics

我想做这样的事情:

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”

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

13 个答案:

答案 0 :(得分:236)

将返回类型更改为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;
}

答案 1 :(得分:101)

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 :(得分:54)

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

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

答案 3 :(得分:5)

免责声明:此答案有效,但仅供教育之用。 :)詹姆斯琼斯的解决方案可能是最好的,当然也是我应该使用的。

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的所有代码一样:caveat coder。

答案 4 :(得分:4)

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

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

答案 5 :(得分:3)

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

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

答案 6 :(得分:2)

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

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

答案 7 :(得分:2)

如果它对某人有帮助 - 我以前使用过它并且似乎可以做我需要的事情......

public static bool HasValueAndIsNotDefault<T>(this T? v)
    where T : struct
{
    return v.HasValue && !v.Value.Equals(default(T));
}

答案 8 :(得分:1)

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

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

非常相似

答案 9 :(得分:1)

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

... = reader["myYear"] as int?;有效且干净。

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

答案 10 :(得分:1)

较短的方法:

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

0返回int,为null返回int?

答案 11 :(得分:0)

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

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

就我而言,我特别希望返回null,而不是任何可能的值类型的默认值。 GetValueOrDefault =坏。 GetValueOrNull = good。

我使用了&#34; Null&#34;和&#34; Nullable&#34;区分引用类型和值类型。这里是我编写的一些扩展方法的示例,它们补充了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;
    }

答案 12 :(得分:0)

在 Java 中,typed param 的可空类型可以定义如下:

Class1<? super ClassTemp1,? super ClassTemp2> uiCallback

Kotlin 的等效代码如下:

Class1<in ClassTemp1?, in ClassTemp2?>