我想这样做:
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;
}
但我现在得到的错误是:
类型'int?'必须是引用类型,以便在泛型类型或方法中使用它作为参数'T'
没错!Nullable<int>是一个结构体!所以我尝试将类约束改为结构约束(作为副作用不能再返回null):
public static T GetValueOrNull<T>(this DbDataRecord reader, string columnName)
where T : struct
现在是作业:
myYear = record.GetValueOrNull<int?>("myYear");
给出以下错误:
类型'int?'必须是一个非空值类型,以便在泛型类型或方法中使用它作为参数'T'
是否可以将可空类型指定为泛型参数?
我认为您需要处理引用类型和结构类型。
我使用它将XML Element字符串转换为更有类型的类型。
您可以使用反射删除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);
免责声明:这个答案是有效的,但仅用于教育目的。James Jones的解决方案可能是最好的,当然也是我想要的。
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,或者一个字符串,或者任何从DB返回的类型。
唯一的问题是,这并不会阻止你在LHS上使用非空类型,在这种情况下,你会得到一个相当讨厌的运行时异常,比如:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Cannot convert null to 'int' because it is a non-nullable value type
与所有使用dynamic: caution编码器的代码一样。