2008-10-16 51 views
220

我想要做这样的事情:可为空类型作为通用参数吗?

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

但现在我得到的错误是:

The type 'int?' must be a reference type in order to use it as parameter 'T' in the generic type or method

权! Nullable<int>struct!所以,我想改变类约束至struct约束(以及它的副作用不能返回null更多):

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

现在的分配:

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

提供了以下错误:

The type 'int?' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method

指定一个可为空的类型作为所有可能的泛型参数?

+1

请请让你的签名`IDataRecord`从`DbDataRecord` .. – nawfal 2013-02-07 05:56:58

回答

205

更改返回类型为空,并呼吁与非可空参数

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

我建议你使用“columnValue ==为DBNull。价值”,而不是‘是’经营者,因为它的速度稍快=) – driAn 2009-03-26 21:33:57

+29

个人喜好,但你可以使用简写形式T',而不是可空 Dunc 2010-09-09 15:04:00

+8

这是罚款值类型,但我认为它不会工作在所有参考类型(例如GetValueOrNull ),因为C#似乎不喜欢可空<(ref type)>像罗伯特·ç巴斯和詹姆斯·琼斯的解决方案,下面,似乎要好得多我,如果这是你需要 – bacar 2011-07-28 10:43:24

39

只需要做两件事你原来的代码的方法 - 删除where约束,并改变从return null最后returnreturn default(T) 。这样你可以返回你想要的任何类型。

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

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; 
} 
91
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); 
3

我认为要处理引用类型和结构类型。 我用它来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; 
      } 
     } 
    } 

您可以使用它像这样:

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

免责声明:这个答案工作,但仅用于教育目的。 :)詹姆斯琼斯的解决方案可能是这里最好的,当然也是我最喜欢的。

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,或者一个字符串,或任何类型从数据库中返回。

唯一的问题是,这并不妨碍您使用的LHS非可空类型,在这种情况下,你会得到一个相当恶劣的运行时异常,如:

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

如同所有的代码,使用dynamic:警告编码器。

2

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

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

1

我只是遇到同样的问题我自己。

... = reader["myYear"] as int?;工程和干净。

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

0

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

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

在我的情况下,我特别想要返回null,而不是任何可能的值类型的默认值。 GetValueOrDefault = bad。 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; 
    } 
相关问题