Вопрос

Я сделал следующий метод расширения ...

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 мс

Таким образом, глядя на эти цифры, эти методы не должны быть первым выбором, когда производительность имеет решающее значение - по крайней мере, не при использовании его для нуля.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top