Question

Je l'ai fait la méthode d'extension suivante ...

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

... i qui utilise par exemple pour les données de lecture comme ceci:

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

Mais il ne fonctionne pas quand je veux jeter un ENUM nullable d'une valeur boxed. Le mieux que je pouvais trouver était ce (code WIP désordre qui ne fonctionne pas):

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

Utilisation:

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

L'erreur de courant est un InvalidCastException lorsqu'il tente de lancer un Int32 nullable au enum. Ce qui est ok, je suppose, mais je ne peux pas savoir comment que je pourrais le faire? Je l'ai essayé de créer une instance de la annulable ENUM T et lui attribuer une valeur, mais je suis bloqué sur la façon exactement cela peut se faire.

Toute personne une idée ou d'une meilleure façon de résoudre ce problème? Est-il même possible de résoudre cela de façon générique? Je l'ai fait beaucoup de recherche à ce sujet, mais je ne l'ai pas trouvé quelque chose d'utile.

Était-ce utile?

La solution

Vous pouvez le faire en invoquant le constructeur pour le type Nullable dont vous avez besoin. Comme ceci:

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

Autres conseils

Avec la réponse de Hans j'ai pu le faire fonctionner et si quelqu'un est intéressé est ici la version fixe:

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

Mais les contrôles / code supplémentaires pour l'ENUM annulable nuit à la performance pour tous les autres types. Avant la méthode d'extension était ~ 2-3 plus lent, il est maintenant ~ 10-15 fois. Le faire 1000000 (millions) fois en utilisant le code ci-dessus:

int Unboxing: 4ms
Unboxing int en utilisant la méthode d'extension: 59ms (avant sans prendre soin de nullable ENUM: 12ms)
Unboxing à nullable ENUM: 5ms
Unboxing à NULLABLE enum en utilisant le procédé d'extension: 3382ms

Alors, en regardant ces chiffres ces méthodes ne devrait pas être le premier choix lorsque la performance est critique -. Au moins pas lorsque vous utilisez pour énumérations nullable

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top