我已经为处理procs返回的数据库值做了一个转换方法。它看起来像这样:
public static T GetVerifiedValue<T>(this IDataRecord record, int index)
{
object value = record[index];
if (value is string)
value = string.IsNullOrEmpty(value as string) ? null : value;
//Trata valores de enuns nuláveis. Um cast direto de um tipo numérico (Int32, por exemplo)
//para um valor de enum lança uma exception InvalidCastException.
Type nullableUnderlyingType = Nullable.GetUnderlyingType(typeof(T));
if (nullableUnderlyingType != null)
{
if (nullableUnderlyingType.IsEnum)
return value == null || value.Equals(DBNull.Value) ? default(T) : (T)Enum.ToObject(nullableUnderlyingType, value);
}
/*
//This is my try on solving the problem, but won't compile
//becouse T has no struct constraint
if (value is ValueType)
{
ValueType structValue = (ValueType)value;
return value.Equals(DBNull.Value) ? default(T) : (T)structValue;
}
*/
return value == null || value.Equals(DBNull.Value) ? default(T) : (T)value;
}
问题是,当数据库返回一个Interger时,value
变量将包含int
,当T
为short
时,我会得到InvalidCastException
1}}。
如何改进此方法来处理这种情况?我希望方法的用户不要担心这种问题,不必重复播放。这可能吗?
答案 0 :(得分:4)
您只能将盒装值类型转换为正确的值类型,然后再次强制转换为其他值类型(如果支持此类转换)。
然而,有Convert
类。如果您在int
中有value
框,则可以将其传递给Convert.ToInt16(value)
并返回short
。由于你可能使用Convert
类(而不是强制转换)的类型数量很少,使用switch
的静态通用辅助方法可以很好地工作,如果你使用{ {1}}。
Convert
我对数据库结果大量使用static bool MaybeConvert<TOutput>(object value, out TOutput result) {
output = default(TOutput);
switch(typeof(TOutput)) {
case typeof(short):
result = Convert.ToInt16(value);
return true;
...
default:
return false;
}
}
,因为有时即使您使用的是32位整数字段,如果进行了一些数学或聚合,它们也会返回为64位整数。使用Convert
比检查类型和自己进行非常具体的演员要容易得多。
答案 1 :(得分:4)
将结果用作dynamic
,然后施放应该在有拳击的情况下工作。
public static T GetValue<T>(this IDataRecord record, int index)
{
dynamic result = record[index];
return (result == null || result == DBNull.Value) ? default(T) : (T)result;
}
顺便说一下,有一个Eric Lippert blog post简要介绍了这个。
答案 2 :(得分:2)
我找到了一种方法(请参阅代码注释以获得解释):
public static T GetVerifiedValue<T>(this IDataRecord record, int index)
{
object value = record[index];
if (value == null || value.Equals(DBNull.Value))
return default(T);
//This handles nullable values, because sometimes there is a need for
//a casting on the underlying value before the final cast
Type nullableUnderlyingType = Nullable.GetUnderlyingType(typeof(T));
if (nullableUnderlyingType != null)
{
if (nullableUnderlyingType.IsEnum)
return (T)Enum.ToObject(nullableUnderlyingType, value);
else
return (T)Convert.ChangeType(value, nullableUnderlyingType);
}
//Enums must be handled before the ValueTypes, becouse
//enums are also ValueTypes and using Convert.ChangeType with it will fail
if (typeof(T).IsEnum)
return (T)Enum.ToObject(typeof(T), value);
//######################################################################
//Here is the trick: as Convert.ChangeType returns an object,
//it is compatible with the unconstrained T. Worked nicely.
if (value is ValueType)
{
ValueType structValue = (ValueType)value;
return (T)Convert.ChangeType(structValue, typeof(T));
}
//######################################################################
if (value is string)
value = string.IsNullOrEmpty(value as string) ? null : value;
return (T)value;
}