web-dev-qa-db-fra.com

Comment essayer la valeur TryParse for Enum?

Je veux écrire une fonction qui peut valider une valeur donnée (transmise sous forme de chaîne) par rapport aux valeurs possibles d'un enum. Dans le cas d'une correspondance, il devrait renvoyer l'instance enum; sinon, il devrait retourner une valeur par défaut.

La fonction ne peut pas utiliser en interne try/catch, ce qui exclut l'utilisation de Enum.Parse, qui lève une exception lorsqu'un argument non valide est fourni.

J'aimerais utiliser quelque chose comme une fonction TryParse pour implémenter ceci:

public static TEnum ToEnum<TEnum>(this string strEnumValue, TEnum defaultValue)
{
   object enumValue;
   if (!TryParse (typeof (TEnum), strEnumValue, out enumValue))
   {
       return defaultValue;
   }
   return (TEnum) enumValue;
}
90
Manish Basantani

Comme d'autres l'ont dit, vous devez implémenter votre propre TryParse. Simon Mourier fournit une mise en œuvre complète qui s’occupe de tout.

Si vous utilisez des champs de type bitfield (c.-à-d. Flags), vous devez également gérer une chaîne telle que "MyEnum.Val1|MyEnum.Val2" qui est une combinaison de deux valeurs enum. Si vous appelez simplement Enum.IsDefined avec cette chaîne, il retournera false, même si Enum.Parse le gère correctement.

Mettre à jour

Comme mentionné par Lisa et Christian dans les commentaires, Enum.TryParse est maintenant disponible pour C # dans .NET4 et versions ultérieures.

MSDN Docs

31

Enum.IsDefined va faire avancer les choses. Il ne sera peut-être pas aussi efficace qu'un TryParse, mais cela fonctionnera sans traitement des exceptions.

public static TEnum ToEnum<TEnum>(this string strEnumValue, TEnum defaultValue)
{
    if (!Enum.IsDefined(typeof(TEnum), strEnumValue))
        return defaultValue;

    return (TEnum)Enum.Parse(typeof(TEnum), strEnumValue);
}

À noter: une méthode TryParse a été ajoutée à .NET 4.0.

100
Thorarin

Voici une implémentation personnalisée de EnumTryParse. Contrairement aux autres implémentations courantes, il prend également en charge une énumération marquée avec l'attribut Flags.

    /// <summary>
    /// Converts the string representation of an enum to its Enum equivalent value. A return value indicates whether the operation succeeded.
    /// This method does not rely on Enum.Parse and therefore will never raise any first or second chance exception.
    /// </summary>
    /// <param name="type">The enum target type. May not be null.</param>
    /// <param name="input">The input text. May be null.</param>
    /// <param name="value">When this method returns, contains Enum equivalent value to the enum contained in input, if the conversion succeeded.</param>
    /// <returns>
    /// true if s was converted successfully; otherwise, false.
    /// </returns>
    public static bool EnumTryParse(Type type, string input, out object value)
    {
        if (type == null)
            throw new ArgumentNullException("type");

        if (!type.IsEnum)
            throw new ArgumentException(null, "type");

        if (input == null)
        {
            value = Activator.CreateInstance(type);
            return false;
        }

        input = input.Trim();
        if (input.Length == 0)
        {
            value = Activator.CreateInstance(type);
            return false;
        }

        string[] names = Enum.GetNames(type);
        if (names.Length == 0)
        {
            value = Activator.CreateInstance(type);
            return false;
        }

        Type underlyingType = Enum.GetUnderlyingType(type);
        Array values = Enum.GetValues(type);
        // some enums like System.CodeDom.MemberAttributes *are* flags but are not declared with Flags...
        if ((!type.IsDefined(typeof(FlagsAttribute), true)) && (input.IndexOfAny(_enumSeperators) < 0))
            return EnumToObject(type, underlyingType, names, values, input, out value);

        // multi value enum
        string[] tokens = input.Split(_enumSeperators, StringSplitOptions.RemoveEmptyEntries);
        if (tokens.Length == 0)
        {
            value = Activator.CreateInstance(type);
            return false;
        }

        ulong ul = 0;
        foreach (string tok in tokens)
        {
            string token = tok.Trim(); // NOTE: we don't consider empty tokens as errors
            if (token.Length == 0)
                continue;

            object tokenValue;
            if (!EnumToObject(type, underlyingType, names, values, token, out tokenValue))
            {
                value = Activator.CreateInstance(type);
                return false;
            }

            ulong tokenUl;
            switch (Convert.GetTypeCode(tokenValue))
            {
                case TypeCode.Int16:
                case TypeCode.Int32:
                case TypeCode.Int64:
                case TypeCode.SByte:
                    tokenUl = (ulong)Convert.ToInt64(tokenValue, CultureInfo.InvariantCulture);
                    break;

                //case TypeCode.Byte:
                //case TypeCode.UInt16:
                //case TypeCode.UInt32:
                //case TypeCode.UInt64:
                default:
                    tokenUl = Convert.ToUInt64(tokenValue, CultureInfo.InvariantCulture);
                    break;
            }

            ul |= tokenUl;
        }
        value = Enum.ToObject(type, ul);
        return true;
    }

    private static char[] _enumSeperators = new char[] { ',', ';', '+', '|', ' ' };

    private static object EnumToObject(Type underlyingType, string input)
    {
        if (underlyingType == typeof(int))
        {
            int s;
            if (int.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(uint))
        {
            uint s;
            if (uint.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(ulong))
        {
            ulong s;
            if (ulong.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(long))
        {
            long s;
            if (long.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(short))
        {
            short s;
            if (short.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(ushort))
        {
            ushort s;
            if (ushort.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(byte))
        {
            byte s;
            if (byte.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(sbyte))
        {
            sbyte s;
            if (sbyte.TryParse(input, out s))
                return s;
        }

        return null;
    }

    private static bool EnumToObject(Type type, Type underlyingType, string[] names, Array values, string input, out object value)
    {
        for (int i = 0; i < names.Length; i++)
        {
            if (string.Compare(names[i], input, StringComparison.OrdinalIgnoreCase) == 0)
            {
                value = values.GetValue(i);
                return true;
            }
        }

        if ((char.IsDigit(input[0]) || (input[0] == '-')) || (input[0] == '+'))
        {
            object obj = EnumToObject(underlyingType, input);
            if (obj == null)
            {
                value = Activator.CreateInstance(type);
                return false;
            }
            value = obj;
            return true;
        }

        value = Activator.CreateInstance(type);
        return false;
    }
19
Simon Mourier

À la fin, vous devez implémenter ceci autour de Enum.GetNames:

public bool TryParseEnum<T>(string str, bool caseSensitive, out T value) where T : struct {
    // Can't make this a type constraint...
    if (!typeof(T).IsEnum) {
        throw new ArgumentException("Type parameter must be an enum");
    }
    var names = Enum.GetNames(typeof(T));
    value = (Enum.GetValues(typeof(T)) as T[])[0];  // For want of a better default
    foreach (var name in names) {
        if (String.Equals(name, str, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase)) {
            value = (T)Enum.Parse(typeof(T), name);
            return true;
        }
    }
    return false;
}

Notes complémentaires:

  • Enum.TryParse est inclus dans .NET 4. Voir ici http://msdn.Microsoft.com/library/dd991876 (VS.100) .aspx
  • Une autre approche consisterait à envelopper directement Enum.Parse attraper l’exception levée en cas d’échec. Cela pourrait être plus rapide quand une correspondance est trouvée, mais sera probablement plus lent sinon. Selon les données que vous traitez, cela peut constituer une amélioration nette ou non.

EDIT: Nous venons de voir une meilleure implémentation de ce problème, qui cache les informations nécessaires: http://damieng.com/blog/2010/10/17/enums-better-syntax-improved-performance -et-tryparse-in-net-3-5

16
Richard

Basé sur .NET 4.5

Exemple de code ci-dessous

using System;

enum Importance
{
    None,
    Low,
    Medium,
    Critical
}

class Program
{
    static void Main()
    {
    // The input value.
    string value = "Medium";

    // An unitialized variable.
    Importance importance;

    // Call Enum.TryParse method.
    if (Enum.TryParse(value, out importance))
    {
        // We now have an enum type.
        Console.WriteLine(importance == Importance.Medium);
    }
    }
}

Référence: http://www.dotnetperls.com/enum-parse

8
Hugo Hilário
enum EnumStatus
{
    NAO_INFORMADO = 0,
    ENCONTRADO = 1,
    BLOQUEADA_PELO_ENTREGADOR = 2,
    DISPOSITIVO_DESABILITADO = 3,
    ERRO_INTERNO = 4,
    AGARDANDO = 5
}

...

if (Enum.TryParse<EnumStatus>(item.status, out status)) {

}
4
Everson Rafael

J'ai une implémentation optimisée que vous pourriez utiliser dans nconstrainedMelody . Effectivement, il s'agit simplement de mettre en cache la liste des noms, mais d'une manière sympa, typée, génériquement contrainte :)

4
Jon Skeet

Il n'y a actuellement aucun Enum.TryParse prêt à l'emploi. Cela a été demandé sur Connect ( Toujours pas d'énum.TryParse ) et une réponse indiquant une possible inclusion dans le prochain framework après .NET 3.5. Vous devrez mettre en œuvre les solutions de contournement suggérées pour l'instant.

2
Ahmad Mageed

Le seul moyen d'éviter la gestion des exceptions consiste à utiliser la méthode GetNames (), et nous savons tous que les exceptions ne doivent pas être utilisées de manière abusive pour la logique d'application courante :)

1
Philippe Leybaert

La mise en cache d'une fonction/dictionnaire généré dynamiquement est-elle autorisée?

Parce que vous ne semblez pas connaître le type d’énumération à l’avance, la première exécution pourrait générer quelque chose qui pourrait en tirer parti.

Vous pouvez même mettre en cache le résultat de Enum.GetNames ()

Essayez-vous d'optimiser pour le processeur ou la mémoire? Avez-vous vraiment besoin de?

1
Nader Shirazie

Comme d'autres l'ont déjà dit, si vous n'utilisez pas Try & Catch, vous devez utiliser IsDefined ou GetNames ... Voici quelques exemples ... ils sont fondamentalement tous identiques, le premier gérant des énumérations nullables. Je préfère le second car il s’agit d’une extension de chaînes, pas d’énums ... mais vous pouvez les mélanger à votre guise!

  • www.objectreference.net/post/Enum-TryParse-Extension-Method.aspx
  • flatlinerdoa.spaces.live.com/blog/cns!17124D03A9A052B0!605.entry
  • mironabramson.com/blog/post/2008/03/Another-version-for-the-missing-method-EnumTryParse.aspx
  • lazyloading.blogspot.com/2008/04/enumtryparse-with-net-35-extension.html
0
Enum

Il n'y a pas de TryParse car le type Enum n'est pas connu avant l'exécution. TryParse qui suit la même méthodologie que la méthode Date.TryParse jetterait une erreur de conversion implicite sur le paramètre ByRef.

Je suggère de faire quelque chose comme ça:

//1 line call to get value
MyEnums enumValue = (Sections)EnumValue(typeof(Sections), myEnumTextValue, MyEnums.SomeEnumDefault);

//Put this somewhere where you can reuse
public static object EnumValue(System.Type enumType, string value, object NotDefinedReplacement)
{
    if (Enum.IsDefined(enumType, value)) {
        return Enum.Parse(enumType, value);
    } else {
        return Enum.Parse(enumType, NotDefinedReplacement);
    }
}
0
ben