我已经做了如下的扩展方法...

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

所以,看这些数字这些方法不应当是第一选择,当性能是至关重要的。 - 使用它为空的枚举时至少不

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top