泛型方法,拆箱为空的枚举
题
我已经做了如下的扩展方法...
public static class ObjectExtensions
{
public static T As<T>(this object pObject, T pDefaultValue)
{
if (pObject == null || pObject == DBNull.Value)
return pDefaultValue;
return (T) pObject;
}
}
...,我使用的例如读取像这样的数据:
string field = datareader["column"].As("default value when null")
但是,当我要投射到一个可空枚举从装箱值这是行不通的。我能想出最好是这样的(凌乱WIP代码不工作):
public static class ObjectExtensions
{
public static T As<T>(this object pObject, T pDefaultValue)
{
if (pObject == null || pObject == DBNull.Value)
return pDefaultValue;
var lType = typeof (T);
if (!IsNullableEnum(lType))
return (T) pObject;
var lEnumType = Nullable.GetUnderlyingType(lType);
var lEnumPrimitiveType = lEnumType.GetEnumUnderlyingType();
if (lEnumPrimitiveType == typeof(int))
{
var lObject = (int?) pObject;
return (T) Convert.ChangeType(lObject, lType);
}
throw new InvalidCastException();
}
private static bool IsNullableEnum(Type pType)
{
Type lUnderlyingType = Nullable.GetUnderlyingType(pType);
return (lUnderlyingType != null) && lUnderlyingType.IsEnum;
}
}
用法:
public enum SomeEnum {Value1, Value2};
object value = 1;
var result = value.As<SomeEnum?>();
在电流误差是一个InvalidCastException当它试图铸造一个Int32到为空的枚举。这是确定我想,但我不知道怎么回事,我能做到这一点?我试图创建可空枚举T的实例,并为它分配一个值,但我卡在究竟如何可以做到这一点。
任何一个想法或一个更好的办法来解决这个问题?它甚至有可能解决,在一个通用的方法?我已经做了很多上搜索的,但我还没有发现任何有用的东西。
解决方案
您可以通过调用构造函数中您需要的可空类型做到这一点。像这样:
Type t = typeof(Nullable<>).MakeGenericType(lEnumType);
var ctor = t.GetConstructor(new Type[] { lEnumType });
return (T)ctor.Invoke(new object[] { pObject });
其他提示
随着汉斯回答我能得到它的工作,如果有人在这里感兴趣的固定的版本:
public static class ObjectExtensions
{
private static Dictionary<Type, ConstructorInfo> _NullableEnumCtor = new Dictionary<Type, ConstructorInfo>();
public static T As<T>(this object pObject)
{
return As(pObject, default(T));
}
public static T As<T>(this object pObject, T pDefaultValue)
{
if (pObject == null || pObject == DBNull.Value)
return pDefaultValue;
var lObjectType = pObject.GetType();
var lTargetType = typeof(T);
if (lObjectType == lTargetType)
return (T) pObject;
var lCtor = GetNullableEnumCtor(lTargetType);
if (lCtor == null)
return (T) pObject;
return (T)lCtor.Invoke(new[] { pObject });
}
private static ConstructorInfo GetNullableEnumCtor(Type pType)
{
if (_NullableEnumCtor.ContainsKey(pType))
return _NullableEnumCtor[pType];
var lUnderlyingType = Nullable.GetUnderlyingType(pType);
if (lUnderlyingType == null || !lUnderlyingType.IsEnum)
{
lock (_NullableEnumCtor) { _NullableEnumCtor.Add(pType, null); }
return null;
}
var lNullableType = typeof(Nullable<>).MakeGenericType(lUnderlyingType);
var lCtor = lNullableType.GetConstructor(new[] { lUnderlyingType });
lock (_NullableEnumCtor) { _NullableEnumCtor.Add(pType, lCtor); }
return lCtor;
}
}
但对于可为空的枚举的额外的检查/代码伤害对于所有其它类型的性能。扩展方法是约2-3较慢之前,现在是〜10-15倍。使用上面的代码做1000000(百万)次:
开箱INT:4ms的结果 使用拆箱INT扩展方法:59ms(之前没有考虑为空的枚举的护理:为12ms)点击 拆箱可空枚举:5ms的结果 使用扩展方法拆箱到为空的枚举:3382ms
所以,看这些数字这些方法不应当是第一选择,当性能是至关重要的。 - 使用它为空的枚举时至少不
不隶属于 StackOverflow