Общий метод, распаковка Nullbable enum
Вопрос
Я сделал следующий метод расширения ...
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")
Но это не работает, когда я хочу отбрасывать до Nullable Enum из корпусной стоимости. Лучше всего я мог бы придумать, был это (грязный 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 на Nullable Enum. Что в порядке, я думаю, но я понятия не имею, как еще я мог это сделать? Я пытался создать экземпляр Nullbable Enum T и назначить его значение, но я застрял на том, насколько именно это можно сделать.
Любой идея или лучший способ решить это? Можно ли решить, что в общем случае? Я сделал довольно много поиска на это, но я не нашел ничего полезного.
Решение
Вы можете сделать это, вызывая конструктор для нуждающегося нуждающегося нуждающегося типа. Так:
Type t = typeof(Nullable<>).MakeGenericType(lEnumType);
var ctor = t.GetConstructor(new Type[] { lEnumType });
return (T)ctor.Invoke(new object[] { pObject });
Другие советы
С ответ Hans я смог заставить его работать, и если кто-то заинтересован, здесь фиксированная версия:
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;
}
}
Но дополнительные проверки / код для Nullbable Enum причиняют вред производительности для всех других типов. Перед методом расширения было ~ 2-3 медленнее, теперь это ~ 10-15 раз. Делать это 1000000 (миллион) времена, используя код выше:
Unboxing int: 4ms
UNBOXING INT Использование метода расширения: 59 мс (до того, как без заботы о Nullbable Enum: 12ms)
Unboxing inultable enum: 5 мс
Unboxing in Nullable Enum с использованием метода расширения: 3382 мс
Таким образом, глядя на эти цифры, эти методы не должны быть первым выбором, когда производительность имеет решающее значение - по крайней мере, не при использовании его для нуля.