简体   繁体   English

可空类型作为泛型参数可能吗?

[英]Nullable type as a generic parameter possible?

I want to do something like this :我想做这样的事情:

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

Notice the nullable type as the generic parameter.请注意可空类型作为泛型参数。

Since the GetValueOrNull function could return null my first attempt was this:由于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;
}

But the error I'm getting now is:但我现在得到的错误是:

The type 'int?' 'int?' 类型must be a reference type in order to use it as parameter 'T' in the generic type or method必须是引用类型才能将其用作泛型类型或方法中的参数“T”

Right!对! Nullable<int> is a struct ! Nullable<int>是一个struct So I tried changing the class constraint to a struct constraint (and as a side effect can't return null any more):所以我尝试将类约束更改为struct约束(并且作为副作用不能再返回null ):

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

Now the assignment:现在任务:

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

Gives the following error:给出以下错误:

The type 'int?' 'int?' 类型must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method必须是不可为 null 的值类型才能将其用作泛型类型或方法中的参数“T”

Is specifying a nullable type as a generic parameter at all possible?是否可能将可为空类型指定为泛型参数?

Change the return type to Nullable<T> , and call the method with the non nullable parameter将返回类型更改为Nullable<T> ,并使用不可为 null 的参数调用该方法

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;
}
public static T GetValueOrDefault<T>(this IDataRecord rdr, int index)
{
    object val = rdr[index];

    if (!(val is DBNull))
        return (T)val;

    return default(T);
}

Just use it like this:只需像这样使用它:

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

Just do two things to your original code – remove the where constraint, and change the last return from return null to return default(T) .只需对原始代码做两件事——删除where约束,并将最后一个returnreturn null更改为return default(T) This way you can return whatever type you want.这样你就可以返回任何你想要的类型。

By the way, you can avoid the use of is by changing your if statement to if (columnValue != DBNull.Value) .顺便说一下,您可以通过将if语句更改为if (columnValue != DBNull.Value)来避免使用is

I think you want to handle Reference types and struct types.我认为您想处理引用类型和结构类型。 I use it to convert XML Element strings to a more typed type.我使用它来将 XML 元素字符串转换为更类型化的类型。 You can remove the nullAlternative with reflection.您可以使用反射删除 nullAlternative。 The formatprovider is to handle the culture dependent '.' formatprovider 用于处理依赖于文化的 '.' or ',' separator in eg decimals or ints and doubles.或 ',' 分隔符,例如小数或整数和双精度数。 This may work:这可能有效:

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

You can use it like this:你可以这样使用它:

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

Disclaimer: This answer works, but is intended for educational purposes only.免责声明:此答案有效,但仅用于教育目的。 :) James Jones' solution is probably the best here and certainly the one I'd go with. :) James Jones 的解决方案可能是这里最好的解决方案,当然也是我会采用的解决方案

C# 4.0's dynamic keyword makes this even easier, if less safe: C# 4.0 的dynamic关键字使这更容易,如果不那么安全:

public static dynamic GetNullableValue(this IDataRecord record, string columnName)
{
  var val = reader[columnName];

  return (val == DBNull.Value ? null : val);
}

Now you don't need the explicit type hinting on the RHS:现在您不需要 RHS 上的显式类型提示:

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

In fact, you don't need it anywhere!事实上,您在任何地方都不需要它!

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

value will now be an int, or a string, or whatever type was returned from the DB. value现在将是一个整数,或一个字符串,或从数据库返回的任何类型。

The only problem is that this does not prevent you from using non-nullable types on the LHS, in which case you'll get a rather nasty runtime exception like:唯一的问题是,这不会阻止您在 LHS 上使用不可为空的类型,在这种情况下,您会得到一个相当讨厌的运行时异常,例如:

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

As with all code that uses dynamic : caveat coder.与所有使用dynamic代码一样:警告编码器。

Just had to do something incredible similar to this.只需要做一些与此类似的令人难以置信的事情。 My code:我的代码:

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

This may be a dead thread, but I tend to use the following:这可能是一个死线程,但我倾向于使用以下内容:

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

I know this is old, but here is another solution:我知道这很旧,但这是另一种解决方案:

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

Now, you don't care if T was value or reference type.现在,您不在乎T是值类型还是引用类型。 Only if the function returns true, you have a reasonable value from the database.只有当函数返回 true 时,您才能从数据库中获得合理的值。 Usage:用法:

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

This approach is very similar to int.TryParse("123", out MyInt);这种方法与int.TryParse("123", out MyInt);非常相似int.TryParse("123", out MyInt);

I just encountered the same problem myself.我自己也遇到了同样的问题。

... = reader["myYear"] as int?; works and is clean.工作并且很干净。

It works with any type without an issue.它适用于任何类型,没有问题。 If the result is DBNull, it returns null as the conversion fails.如果结果为 DBNull,则在转换失败时返回 null。

The shorter way :较短的方法:

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

return 0 for int , and null for int?int返回0int返回null int?

Incase it helps someone - I have used this before and seems to do what I need it to...以防它对某人有所帮助-我以前使用过它,并且似乎可以满足我的需要...

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

Multiple generic constraints can't be combined in an OR fashion (less restrictive), only in an AND fashion (more restrictive).多个通用约束不能以 OR 方式(限制较少)组合,只能以 AND 方式(限制较多)组合。 Meaning that one method can't handle both scenarios.这意味着一种方法不能处理这两种情况。 The generic constraints also cannot be used to make a unique signature for the method, so you'd have to use 2 separate method names.通用约束也不能用于为方法制作唯一签名,因此您必须使用 2 个单独的方法名称。

However, you can use the generic constraints to make sure that the methods are used correctly.但是,您可以使用通用约束来确保正确使用这些方法。

In my case, I specifically wanted null to be returned, and never the default value of any possible value types.就我而言,我特别希望返回 null,而不是任何可能的值类型的默认值。 GetValueOrDefault = bad. GetValueOrDefault = 不好。 GetValueOrNull = good. GetValueOrNull = 好。

I used the words "Null" and "Nullable" to distinguish between reference types and value types.我使用“Null”和“Nullable”这两个词来区分引用类型和值类型。 And here is an example of a couple extension methods I wrote that compliments the FirstOrDefault method in System.Linq.Enumerable class.这是我编写的几个扩展方法的示例,它们补充了 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;
    }

In Java nullable type for typed param can be defined like below:在 Java 中,typed param 的可空类型可以定义如下:

Class1<? super ClassTemp1,? super ClassTemp2> uiCallback

Equivalent code for Kotlin will be like this: Kotlin 的等效代码如下所示:

Class1<in ClassTemp1?, in ClassTemp2?>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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