General method, unboxing nullable enum

I made the following extension method ...

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;
    }
}

... which I use, for example, reading data as follows:

string field = datareader["column"].As("default value when null")

But this will not work when I want to translate to a null enumeration from the value in the box. The best I could come up with is (dirty WIP code that doesn't work):

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;
    }
}

Using:

public enum SomeEnum {Value1, Value2};
object value = 1;
var result = value.As<SomeEnum?>();

The current error is an InvalidCastException when it tries to pass Int32 to a null enumeration. Which is normal, I think, but I don’t know how else could I do this? I tried to create an instance of nullable enum T and assign a value to it, but I was fixated on how exactly this can be done.

Any idea or best way to solve this problem? Is it possible to solve this in general? I searched a lot, but I did not find anything useful.

+3
2

, , . :

            Type t = typeof(Nullable<>).MakeGenericType(lEnumType);
            var ctor = t.GetConstructor(new Type[] { lEnumType });
            return (T)ctor.Invoke(new object[] { pObject });
+3

, - :

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 () , :

Unboxing int: 4ms
Unboxing int : 59 ( , : 12 )
Unboxing to nullable : 5ms
Unboxing to nullable enum : 3382

, , , - , .

0

Source: https://habr.com/ru/post/1762306/


All Articles