可以使用可空types作为generics参数?

我想要做这样的事情:

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

注意可空types作为generics参数。

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

但是我现在得到的错误是:

types'int?' 必须是引用types才能在通用types或方法中将其用作参数“T”

对! Nullable<int>是一个struct ! 所以我试着改变类约束到一个struct约束(和副作用不能返回null ):

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

现在的任务:

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

给出以下错误:

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

尽可能指定一个可为空的types作为generics参数?

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

只要做两件事你的原始代码。 删除where约束,并将最后一次返回值从null返回为default(T)。 这样你可以返回你想要的任何types。

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

只是不得不做了类似这样的令人难以置信的事情。 我的代码:

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

免责声明:此答案适用,但仅限于教育目的。 :)詹姆斯·琼斯的解决scheme可能是这里最好的,当然也是我想要的。

如果安全性较低,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,或者一个string,或者是从DB返回的任何types。

唯一的问题是,这并不妨碍你在LHS上使用不可空的types,在这种情况下,你会得到一个相当讨厌的运行时exception,如:

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

与所有使用dynamic代码的代码一样:

我想你想要处理引用types和结构types。 我使用它来将XML元素string转换为更具types的types。 您可以使用reflection删除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); 

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

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

我知道这是旧的,但这是另一个解决scheme:

 public static bool GetValueOrDefault<T>(this SqlDataReader rdr, 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);

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

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

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

多个通用约束不能以OR方式(限制较less)进行组合,只能以AND方式(更具限制性)进行组合。 这意味着一种方法不能处理这两种情况。 通用的约束也不能用来为方法做一个唯一的签名,所以你必须使用2个独立的方法名称。

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

在我的情况下,我特别想要返回null,而不是任何可能的值types的默认值。 GetValueOrDefault =坏。 GetValueOrNull =好。

我用“Null”和“Nullable”来区分引用types和值types。 这里有一个我写的几个扩展方法的例子,它赞扬了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; }