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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/209160/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 18:10:09  来源:igfitidea点击:

Nullable type as a generic parameter possible?

c#generics

提问by Tom Pester

I want to do something like this :

我想做这样的事情:

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

Notice the nullable type as the generic parameter.

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

Since the GetValueOrNullfunction 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?' must be a reference type in order to use it as parameter 'T' in the generic type or method

'int?' 类型 必须是引用类型才能将其用作泛型类型或方法中的参数“T”

Right! Nullable<int>is a struct! So I tried changing the class constraint to a structconstraint (and as a side effect can't return nullany more):

对!Nullable<int>是一个struct!所以我尝试将类约束更改为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?' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method

'int?' 类型 必须是不可为 null 的值类型才能将其用作泛型类型或方法中的参数“T”

Is specifying a nullable type as a generic parameter at all possible?

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

采纳答案by Greg Dean

Change the return type to Nullable, and call the method with the non nullable parameter

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

回答by Robert C. Barth

Just do two things to your original code – remove the whereconstraint, and change the last returnfrom return nullto return default(T). This way you can return whatever type you want.

只需对原始代码做两件事 - 删除where约束,并将最后一个return从更改return nullreturn default(T)。这样你就可以返回任何你想要的类型。

By the way, you can avoid the use of isby changing your ifstatement to if (columnValue != DBNull.Value).

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

回答by Toby

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

回答by James Jones

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

回答by Roland Roos

I think you want to handle Reference types and struct types. I use it to convert XML Element strings to a more typed type. You can remove the nullAlternative with reflection. The formatprovider is to handle the culture dependent '.' or ',' separator in e.g. decimals or ints and doubles. This may work:

我认为您想处理引用类型和结构类型。我使用它来将 XML 元素字符串转换为更类型化的类型。您可以使用反射删除 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; 
            }
        }
    }

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

回答by Ian Kemp

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 dynamickeyword 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 even need it at all!

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

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

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

回答by Ryan Horch

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

回答by nurchi

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 Twas value or reference type. Only if the function returns true, you have a reasonable value from the database. Usage:

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

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

回答by Hele

I just encountered the same problem myself.

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

... = reader["myYear"] as int?;works and is clean.

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

It works with any type without an issue. If the result is DBNull, it returns null as the conversion fails.

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

回答by Casey Plummer

Multiple generic constraints can't be combined in an OR fashion (less restrictive), only in an AND fashion (more restrictive). 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.

多个通用约束不能以 OR 方式(限制较少)组合,只能以 AND 方式(限制较多)组合。这意味着一种方法不能处理这两种情况。通用约束也不能用于为方法制作唯一签名,因此您必须使用 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. GetValueOrDefault = bad. GetValueOrNull = good.

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

I used the words "Null" and "Nullable" to distinguish between reference types and value types. And here is an example of a couple extension methods I wrote that compliments the FirstOrDefault method in System.Linq.Enumerable class.

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