Nullable type as a generic parameter possible?
我想这样做:
1 | myYear = record.GetValueOrNull<int?>("myYear"), |
请注意,可以为空的类型是泛型参数。
由于
1 2 3 4 5 6 7 8 9 10 11 | 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
正确的!
1 2 | public static T GetValueOrNull<T>(this DbDataRecord reader, string columnName) where T : struct |
现在的任务是:
1 | 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
是否尽可能将可为空的类型指定为泛型参数?
将返回类型更改为nullable,并使用不可为nullable的参数调用方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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 2 3 4 5 6 7 8 9 | public static T GetValueOrDefault<T>(this IDataRecord rdr, int index) { object val = rdr[index]; if (!(val is DBNull)) return (T)val; return default(T); } |
就这样使用:
1 2 | decimal? Quantity = rdr.GetValueOrDefault<decimal?>(1); string Unit = rdr.GetValueOrDefault<string>(2); |
只需对原始代码做两件事—删除
另外,您可以通过将您的
免责声明:此答案有效,但仅用于教育目的。:)詹姆斯·琼斯的解决方案可能是这里最好的,当然也是我愿意采用的解决方案。
C 4.0的
1 2 3 4 5 6 | public static dynamic GetNullableValue(this IDataRecord record, string columnName) { var val = reader[columnName]; return (val == DBNull.Value ? null : val); } |
现在,您不需要在rhs上使用显式类型提示:
1 | int? value = myDataReader.GetNullableValue("MyColumnName"); |
事实上,你根本不需要它!
1 | var value = myDataReader.GetNullableValue("MyColumnName"); |
唯一的问题是,这并不能阻止您在lhs上使用不可为空的类型,在这种情况下,您将得到一个非常讨厌的运行时异常,比如:
1 | Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Cannot convert null to 'int' because it is a non-nullable value type |
和所有使用
只是做了一件不可思议的类似的事情。我的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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; } |
我认为您需要处理引用类型和结构类型。我使用它将XML元素字符串转换为更类型化的类型。可以通过反射移除nullAlternative。格式提供程序将处理与区域性相关的"."或","分隔符,例如小数或整数和双精度数。这可能会起作用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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; } } } |
您可以这样使用它:
1 2 3 4 5 6 7 8 9 10 | 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); |
这可能是一个死线程,但我倾向于使用以下内容:
1 2 3 4 5 | public static T? GetValueOrNull<T>(this DbDataRecord reader, string columnName) where T : struct { return reader[columnName] as T?; } |
我自己也遇到了同样的问题。
它适用于任何类型,没有问题。如果结果为dbnull,则在转换失败时返回null。
我知道这很古老,但还有另一个解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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; } } |
现在,您不关心
1 2 3 4 5 6 | ... decimal Quantity; if (rdr.GetValueOrDefault<decimal>("YourColumnName", out Quantity)) { // Do something with Quantity } |
这种方法与
多个通用约束不能以"或"方式组合(限制性较小),只能以"和"方式组合(限制性更强)。这意味着一个方法不能同时处理这两个场景。泛型约束也不能用于为方法生成唯一的签名,因此必须使用两个单独的方法名。
但是,可以使用泛型约束来确保正确使用方法。
在我的例子中,我特别希望返回空值,而不是任何可能值类型的默认值。GetValueOrDefault=错误。getValueOrNull=好。
我使用"null"和"nullable"来区分引用类型和值类型。下面是我编写的几个扩展方法的一个例子,它们对System.Linq.Enumerable类中的FirstOrDefault方法进行了补充。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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; } |