Comment TryParse pour la valeur Enum?

Je veux écrire une fonction capable de 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’il reçoit un argument non valide.

Je voudrais utiliser quelque chose comme une fonction TryParse pour implémenter ceci:

 public static TEnum ToEnum(this ssortingng strEnumValue, TEnum defaultValue) { object enumValue; if (!TryParse (typeof (TEnum), strEnumValue, out enumValue)) { return defaultValue; } return (TEnum) enumValue; } 

Comme d’autres l’ont dit, vous devez implémenter votre propre TryParse . Simon Mourier fournit une implémentation complète qui prend tout en charge.

Si vous utilisez des énumérations de champs de bits (c.-à-d. Des indicateurs), 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, elle retournera false, même si Enum.Parse gère correctement.

Mettre à jour

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

MSDN Docs

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

 public static TEnum ToEnum(this ssortingng strEnumValue, TEnum defaultValue) { if (!Enum.IsDefined(typeof(TEnum), strEnumValue)) return defaultValue; return (TEnum)Enum.Parse(typeof(TEnum), strEnumValue); } 

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

Voici une implémentation personnalisée de EnumTryParse . Contrairement à d’autres implémentations courantes, il prend également en charge enum marqué avec l’atsortingbut Flags .

  ///  /// Converts the ssortingng 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. ///  /// The enum target type. May not be null. /// The input text. May be null. /// When this method returns, contains Enum equivalent value to the enum contained in input, if the conversion succeeded. ///  /// true if s was converted successfully; otherwise, false. ///  public static bool EnumTryParse(Type type, ssortingng 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; } ssortingng[] 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.MemberAtsortingbutes *are* flags but are not declared with Flags... if ((!type.IsDefined(typeof(FlagsAtsortingbute), 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; } 

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

 public bool TryParseEnum(ssortingng 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 (Ssortingng.Equals(name, str, caseSensitive ? SsortingngComparison.Ordinal : SsortingngComparison.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 serait d’envelopper directement Enum.Parse attraper l’exception levée quand elle échoue. Cela pourrait être plus rapide quand une correspondance est trouvée, mais sera probablement plus lente sinon. Selon les données que vous traitez, cela peut être une amélioration nette ou non.

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

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. ssortingng 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

J’ai une implémentation optimisée que vous pourriez utiliser dans UnconstrainedMelody . Effectivement, il ne s’agit que de mettre en cache la liste des noms, mais cela se fait de manière géniale, fortement typée et générique 🙂

Enum.TryParse n’est pas disponible immédiatement. Cela a été demandé sur Connect ( toujours pas Enum.TryParse ) et a reçu une réponse indiquant une possible inclusion dans le prochain framework après .NET 3.5. Vous devrez implémenter les solutions de contournement suggérées pour le moment.

La seule façon d’éviter la gestion des exceptions est d’utiliser la méthode GetNames () et nous soaps tous que les exceptions ne doivent pas être utilisées de manière abusive pour la logique d’application commune 🙂

La mise en cache d’une fonction / d’un dictionnaire généré dynamicment est-elle autorisée?

Comme vous ne semblez pas (semble-t-il) connaître le type de l’énumération à l’avance, la première exécution pourrait générer quelque chose dont les exécutions ultérieures pourraient 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? En avez-vous vraiment besoin?

 enum EnumStatus { NAO_INFORMADO = 0, ENCONTRADO = 1, BLOQUEADA_PELO_ENTREGADOR = 2, DISPOSITIVO_DESABILITADO = 3, ERRO_INTERNO = 4, AGARDANDO = 5 } 

 if (Enum.TryParse(item.status, out status)) { 

}

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 les mêmes, le premier à gérer les énumérations nullables. Je préfère le 2ème car c’est une extension sur cordes, pas de énumération … mais vous pouvez les mixer comme vous le souhaitez!

Il n’y a pas de TryParse car le type de Enum n’est pas connu avant l’exécution. Un TryParse qui suit la même méthodologie que la méthode Date.TryParse émettrait 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, ssortingng value, object NotDefinedReplacement) { if (Enum.IsDefined(enumType, value)) { return Enum.Parse(enumType, value); } else { return Enum.Parse(enumType, NotDefinedReplacement); } } 

Regardez la classe Enum (struct?) Elle-même. Il y a une méthode Parse à ce sujet mais je ne suis pas sûr pour un tryparse.

Cette méthode convertira un type de enum:

  public static TEnum ToEnum(object EnumValue, TEnum defaultValue) { if (!Enum.IsDefined(typeof(TEnum), EnumValue)) { Type enumType = Enum.GetUnderlyingType(typeof(TEnum)); if ( EnumValue.GetType() == enumType ) { ssortingng name = Enum.GetName(typeof(HLink.ViewModels.ClaimHeaderViewModel.ClaimStatus), EnumValue); if( name != null) return (TEnum)Enum.Parse(typeof(TEnum), name); return defaultValue; } } return (TEnum)Enum.Parse(typeof(TEnum), EnumValue.ToSsortingng()); } 

Il vérifie le type sous-jacent et obtient le nom à parsingr. Si tout échoue, la valeur par défaut sera renvoyée.