可以使用可空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; }