Question

Quelle est la meilleure façon de convertir une chaîne en valeur d’énumération en C# ?

J'ai une balise de sélection HTML contenant les valeurs d'une énumération.Lorsque la page est publiée, je souhaite récupérer la valeur (qui sera sous la forme d'une chaîne) et la convertir en valeur d'énumération.

Dans un monde idéal, je pourrais faire quelque chose comme ceci :

StatusEnum MyStatus = StatusEnum.Parse("Active");

mais ce n'est pas un code valide.

Était-ce utile?

La solution

Dans .NET Core et .NET >4 il existe une méthode d'analyse générique:

Enum.TryParse("Active", out StatusEnum myStatus);

Cela inclut également le nouveau module en ligne de C#7 out variables, donc ceci effectue l'analyse syntaxique, la conversion vers le type enum explicite et initialise + remplit le myStatus variable.

Si vous avez accès à C#7 et au dernier .NET, c'est le meilleur moyen.

Réponse originale

En .NET, c'est plutôt moche (jusqu'à 4 ou plus) :

StatusEnum MyStatus = (StatusEnum) Enum.Parse(typeof(StatusEnum), "Active", true);

J'ai tendance à simplifier cela avec :

public static T ParseEnum<T>(string value)
{
    return (T) Enum.Parse(typeof(T), value, true);
}

Ensuite je peux faire :

StatusEnum MyStatus = EnumUtil.ParseEnum<StatusEnum>("Active");

Une option suggérée dans les commentaires consiste à ajouter une extension, ce qui est assez simple :

public static T ToEnum<T>(this string value)
{
    return (T) Enum.Parse(typeof(T), value, true);
}

StatusEnum MyStatus = "Active".ToEnum<StatusEnum>();

Enfin, vous souhaiterez peut-être avoir une énumération par défaut à utiliser si la chaîne ne peut pas être analysée :

public static T ToEnum<T>(this string value, T defaultValue) 
{
    if (string.IsNullOrEmpty(value))
    {
        return defaultValue;
    }

    T result;
    return Enum.TryParse<T>(value, true, out result) ? result : defaultValue;
}

Ce qui en fait l'appel :

StatusEnum MyStatus = "Active".ToEnum(StatusEnum.None);

Cependant, je serais prudent en ajoutant une méthode d'extension comme celle-ci à string car (sans contrôle d'espace de noms), il apparaîtra sur toutes les instances de string qu'ils détiennent une énumération ou non (donc 1234.ToString().ToEnum(StatusEnum.None) serait valable mais absurde).Il est souvent préférable d'éviter d'encombrer les classes principales de Microsoft avec des méthodes supplémentaires qui ne s'appliquent que dans des contextes très spécifiques, à moins que toute votre équipe de développement n'ait une très bonne compréhension de ce que font ces extensions.

Autres conseils

Utiliser Enum.TryParse<T>(String, T) (≥ .NET 4.0) :

StatusEnum myStatus;
Enum.TryParse("Active", out myStatus);

Cela peut être encore simplifié avec C# 7.0 inlining du type de paramètre:

Enum.TryParse("Active", out StatusEnum myStatus);

Notez que les performances d'Enum.Parse() sont horribles, car elles sont implémentées via la réflexion.(Il en va de même pour Enum.ToString, qui va dans l'autre sens.)

Si vous devez convertir des chaînes en énumérations dans un code sensible aux performances, le mieux est de créer un Dictionary<String,YourEnum> au démarrage et utilisez-le pour effectuer vos conversions.

Vous cherchez Enum.Parse.

SomeEnum enum = (SomeEnum)Enum.Parse(typeof(SomeEnum), "EnumValue");

Vous pouvez utiliser méthodes d'extension maintenant:

public static T ToEnum<T>(this string value, bool ignoreCase = true)
{
    return (T) Enum.Parse(typeof (T), value, ignoreCase);
}

Et vous pouvez les appeler par le code ci-dessous (ici, FilterType est un type énumération ):

FilterType filterType = type.ToEnum<FilterType>();
object Enum.Parse(System.Type enumType, string value, bool ignoreCase);

Donc, si vous aviez une énumération nommée mood, elle ressemblerait à ceci :

   enum Mood
   {
      Angry,
      Happy,
      Sad
   } 

   // ...
   Mood m = (Mood) Enum.Parse(typeof(Mood), "Happy", true);
   Console.WriteLine("My mood is: {0}", m.ToString());

MÉFIEZ-VOUS:

enum Example
{
    One = 1,
    Two = 2,
    Three = 3
}

Enum.(Try)Parse() accepte plusieurs arguments séparés par des virgules et les combine avec un « ou » binaire |.Vous ne pouvez pas désactiver cela et, à mon avis, vous n’en voulez presque jamais.

var x = Enum.Parse("One,Two"); // x is now Three

Même si Three n'a pas été défini, x aurait toujours une valeur int 3.C'est encore pire :Enum.Parse() peut vous donner une valeur qui n'est même pas définie pour l'énumération !

Je ne voudrais pas subir les conséquences du fait que les utilisateurs, volontairement ou involontairement, déclenchent ce comportement.

De plus, comme mentionné par d'autres, les performances sont loin d'être idéales pour les grandes énumérations, à savoir linéaires dans le nombre de valeurs possibles.

Je suggère ce qui suit :

    public static bool TryParse<T>(string value, out T result)
        where T : struct
    {
        var cacheKey = "Enum_" + typeof(T).FullName;

        // [Use MemoryCache to retrieve or create&store a dictionary for this enum, permanently or temporarily.
        // [Implementation off-topic.]
        var enumDictionary = CacheHelper.GetCacheItem(cacheKey, CreateEnumDictionary<T>, EnumCacheExpiration);

        return enumDictionary.TryGetValue(value.Trim(), out result);
    }

    private static Dictionary<string, T> CreateEnumDictionary<T>()
    {
        return Enum.GetValues(typeof(T))
            .Cast<T>()
            .ToDictionary(value => value.ToString(), value => value, StringComparer.OrdinalIgnoreCase);
    }

Enum.Parse est votre ami:

StatusEnum MyStatus = (StatusEnum)Enum.Parse(typeof(StatusEnum), "Active");

Vous pouvez étendre la réponse acceptée avec une valeur par défaut pour éviter les exceptions :

public static T ParseEnum<T>(string value, T defaultValue) where T : struct
{
    try
    {
        T enumValue;
        if (!Enum.TryParse(value, true, out enumValue))
        {
            return defaultValue;
        }
        return enumValue;
    }
    catch (Exception)
    {
        return defaultValue;
    }
}

Ensuite, vous l'appelez comme :

StatusEnum MyStatus = EnumUtil.ParseEnum("Active", StatusEnum.None);

Nous ne pouvions pas supposer une entrée parfaitement valide et avons opté pour cette variante de la réponse de @Keith :

public static TEnum ParseEnum<TEnum>(string value) where TEnum : struct
{
    TEnum tmp; 
    if (!Enum.TryParse<TEnum>(value, true, out tmp))
    {
        tmp = new TEnum();
    }
    return tmp;
}
// str.ToEnum<EnumType>()
T static ToEnum<T>(this string str) 
{ 
    return (T) Enum.Parse(typeof(T), str);
}

Analyse la chaîne en TEnum sans try/catch et sans méthode TryParse() à partir de .NET 4.5

/// <summary>
/// Parses string to TEnum without try/catch and .NET 4.5 TryParse()
/// </summary>
public static bool TryParseToEnum<TEnum>(string probablyEnumAsString_, out TEnum enumValue_) where TEnum : struct
{
    enumValue_ = (TEnum)Enum.GetValues(typeof(TEnum)).GetValue(0);
    if(!Enum.IsDefined(typeof(TEnum), probablyEnumAsString_))
        return false;

    enumValue_ = (TEnum) Enum.Parse(typeof(TEnum), probablyEnumAsString_);
    return true;
}

Code super simple utilisant TryParse :

var value = "Active";

StatusEnum status;
if (!Enum.TryParse<StatusEnum>(value, out status))
    status = StatusEnum.Unknown;

J'aime la solution de la méthode d'extension.

namespace System
{
    public static class StringExtensions
    {

        public static bool TryParseAsEnum<T>(this string value, out T output) where T : struct
        {
            T result;

            var isEnum = Enum.TryParse(value, out result);

            output = isEnum ? result : default(T);

            return isEnum;
        }
    }
}

Ci-dessous mon implémentation avec des tests.

using static Microsoft.VisualStudio.TestTools.UnitTesting.Assert;
using static System.Console;

private enum Countries
    {
        NorthAmerica,
        Europe,
        Rusia,
        Brasil,
        China,
        Asia,
        Australia
    }

   [TestMethod]
        public void StringExtensions_On_TryParseAsEnum()
        {
            var countryName = "Rusia";

            Countries country;
            var isCountry = countryName.TryParseAsEnum(out country);

            WriteLine(country);

            IsTrue(isCountry);
            AreEqual(Countries.Rusia, country);

            countryName = "Don't exist";

            isCountry = countryName.TryParseAsEnum(out country);

            WriteLine(country);

            IsFalse(isCountry);
            AreEqual(Countries.NorthAmerica, country); // the 1rst one in the enumeration
        }
public static T ParseEnum<T>(string value)            //function declaration  
{
    return (T) Enum.Parse(typeof(T), value);
}

Importance imp = EnumUtil.ParseEnum<Importance>("Active");   //function call

====================Un programme complet=====================

using System;

class Program
{
    enum PetType
    {
    None,
    Cat = 1,
    Dog = 2
    }

    static void Main()
    {

    // Possible user input:
    string value = "Dog";

    // Try to convert the string to an enum:
    PetType pet = (PetType)Enum.Parse(typeof(PetType), value);

    // See if the conversion succeeded:
    if (pet == PetType.Dog)
    {
        Console.WriteLine("Equals dog.");
    }
    }
}
-------------
Output

Equals dog.

J'ai utilisé la classe (version fortement typée d'Enum avec des améliorations d'analyse et de performances).Je l'ai trouvé sur GitHub et cela devrait également fonctionner pour .NET 3.5.Il a une certaine surcharge de mémoire car il met en mémoire tampon un dictionnaire.

StatusEnum MyStatus = Enum<StatusEnum>.Parse("Active");

Le billet de blog est Enums – Meilleure syntaxe, performances améliorées et TryParse dans NET 3.5.

Et coder :https://github.com/damieng/DamienGKit/blob/master/CSharp/DamienG.Library/System/EnumT.cs

Pour les performances, cela pourrait aider :

    private static Dictionary<Type, Dictionary<string, object>> dicEnum = new Dictionary<Type, Dictionary<string, object>>();
    public static T ToEnum<T>(this string value, T defaultValue)
    {
        var t = typeof(T);
        Dictionary<string, object> dic;
        if (!dicEnum.ContainsKey(t))
        {
            dic = new Dictionary<string, object>();
            dicEnum.Add(t, dic);
            foreach (var en in Enum.GetValues(t))
                dic.Add(en.ToString(), en);
        }
        else
            dic = dicEnum[t];
        if (!dic.ContainsKey(value))
            return defaultValue;
        else
            return (T)dic[value];
    }

J'ai trouvé qu'ici, le cas des valeurs enum ayant la valeur EnumMember n'était pas pris en compte.Alors c'est parti :

using System.Runtime.Serialization;

public static TEnum ToEnum<TEnum>(this string value, TEnum defaultValue) where TEnum : struct
{
    if (string.IsNullOrEmpty(value))
    {
        return defaultValue;
    }

    TEnum result;
    var enumType = typeof(TEnum);
    foreach (var enumName in Enum.GetNames(enumType))
    {
        var fieldInfo = enumType.GetField(enumName);
        var enumMemberAttribute = ((EnumMemberAttribute[]) fieldInfo.GetCustomAttributes(typeof(EnumMemberAttribute), true)).FirstOrDefault();
        if (enumMemberAttribute?.Value == value)
        {
            return Enum.TryParse(enumName, true, out result) ? result : defaultValue;
        }
    }

    return Enum.TryParse(value, true, out result) ? result : defaultValue;
}

Et exemple de cette énumération :

public enum OracleInstanceStatus
{
    Unknown = -1,
    Started = 1,
    Mounted = 2,
    Open = 3,
    [EnumMember(Value = "OPEN MIGRATE")]
    OpenMigrate = 4
}

Vous devez utiliser Enum.Parse pour obtenir la valeur de l'objet à partir d'Enum, après quoi vous devez modifier la valeur de l'objet en une valeur d'énumération spécifique.La conversion en valeur enum peut être effectuée en utilisant Convert.ChangeType.Veuillez consulter l'extrait de code suivant

public T ConvertStringValueToEnum<T>(string valueToParse){
    return Convert.ChangeType(Enum.Parse(typeof(T), valueToParse, true), typeof(T));
}

Essayez cet exemple :

 public static T GetEnum<T>(string model)
    {
        var newModel = GetStringForEnum(model);

        if (!Enum.IsDefined(typeof(T), newModel))
        {
            return (T)Enum.Parse(typeof(T), "None", true);
        }

        return (T)Enum.Parse(typeof(T), newModel.Result, true);
    }

    private static Task<string> GetStringForEnum(string model)
    {
        return Task.Run(() =>
        {
            Regex rgx = new Regex("[^a-zA-Z0-9 -]");
            var nonAlphanumericData = rgx.Matches(model);
            if (nonAlphanumericData.Count < 1)
            {
                return model;
            }
            foreach (var item in nonAlphanumericData)
            {
                model = model.Replace((string)item, "");
            }
            return model;
        });
    }

Dans cet exemple, vous pouvez envoyer chaque chaîne et définir votre Enum.Si ton Enum vous aviez les données que vous vouliez, renvoyez-les comme votre Enum taper.

        <Extension()>
    Public Function ToEnum(Of TEnum)(ByVal value As String, ByVal defaultValue As TEnum) As TEnum
        If String.IsNullOrEmpty(value) Then
            Return defaultValue
        End If

        Return [Enum].Parse(GetType(TEnum), value, True)
    End Function
public TEnum ToEnum<TEnum>(this string value, TEnum defaultValue){
if (string.IsNullOrEmpty(value))
    return defaultValue;

return Enum.Parse(typeof(TEnum), value, true);}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top