Pergunta

Qual é a melhor maneira de converter uma string em um valor de enumeração em C#?

Eu tenho uma tag de seleção HTML contendo os valores de uma enumeração.Quando a página for postada, quero pegar o valor (que estará na forma de uma string) e convertê-lo para o valor de enumeração.

Em um mundo ideal, eu poderia fazer algo assim:

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

mas esse não é um código válido.

Foi útil?

Solução

No .NET Core e .NET >4 existe um método de análise genérico:

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

Isso também inclui o novo inline do C#7 out variáveis, então isso faz a tentativa de análise, conversão para o tipo enum explícito e inicializa + preenche o myStatus variável.

Se você tiver acesso ao C#7 e ao .NET mais recente, este é o melhor caminho.

Resposta original

No .NET é bastante feio (até 4 ou superior):

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

Costumo simplificar isso com:

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

Então eu posso fazer:

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

Uma opção sugerida nos comentários é adicionar uma extensão, que é bastante simples:

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

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

Finalmente, você pode querer ter um enum padrão para usar se a string não puder ser analisada:

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

O que torna esta a chamada:

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

No entanto, eu teria cuidado ao adicionar um método de extensão como este para string como (sem controle de namespace) ele aparecerá em todas as instâncias de string se eles possuem um enum ou não (então 1234.ToString().ToEnum(StatusEnum.None) seria válido, mas sem sentido).Geralmente é melhor evitar sobrecarregar as classes principais da Microsoft com métodos extras que só se aplicam em contextos muito específicos, a menos que toda a sua equipe de desenvolvimento tenha um bom entendimento do que essas extensões fazem.

Outras dicas

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

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

Pode ser simplificado ainda mais com C# 7.0 tipo de parâmetro inlining:

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

Observe que o desempenho de Enum.Parse() é péssimo, porque é implementado via reflexão.(O mesmo se aplica a Enum.ToString, que segue o caminho inverso.)

Se você precisar converter strings em Enums em código sensível ao desempenho, sua melhor aposta é criar um Dictionary<String,YourEnum> na inicialização e use isso para fazer suas conversões.

Você está procurando Enum.Parse.

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

Você pode usar métodos de extensão agora:

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

E você pode chamá-los pelo código abaixo (aqui, FilterType é um tipo enum):

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

Então, se você tivesse um enum chamado mood, ficaria assim:

   enum Mood
   {
      Angry,
      Happy,
      Sad
   } 

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

CUIDADO:

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

Enum.(Try)Parse() aceita vários argumentos separados por vírgula e os combina com binário 'ou' |.Você não pode desativar isso e, na minha opinião, quase nunca o deseja.

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

Ainda que Three não foi definido, x ainda obteria valor int 3.Isso é ainda pior:Enum.Parse() pode fornecer um valor que nem está definido para o enum!

Eu não gostaria de sofrer as consequências dos usuários, voluntária ou involuntariamente, desencadeando esse comportamento.

Além disso, conforme mencionado por outros, o desempenho não é o ideal para enumerações grandes, ou seja, linear no número de valores possíveis.

Eu sugiro o seguinte:

    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 é seu amigo:

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

Você pode estender a resposta aceita com um valor padrão para evitar exceções:

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

Então você chama assim:

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

Não poderíamos assumir uma entrada perfeitamente válida e optamos por esta variação da resposta 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);
}

Analisa string para TEnum sem try/catch e sem o método TryParse() do .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;
}

Código super simples usando TryParse:

var value = "Active";

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

Eu gosto da solução do método de extensão.

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

Aqui abaixo minha implementação com testes.

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

==================== Um programa completo ====================

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.

Usei class (versão fortemente digitada do Enum com análise e melhorias de desempenho).Encontrei-o no GitHub e também deve funcionar no .NET 3.5.Tem alguma sobrecarga de memória, pois armazena em buffer um dicionário.

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

A postagem do blog é Enums – Melhor sintaxe, desempenho aprimorado e TryParse no NET 3.5.

E código:https://github.com/damieng/DamienGKit/blob/master/CSharp/DamienG.Library/System/EnumT.cs

Para desempenho, isso pode ajudar:

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

Descobri que aqui o caso com valores enum que possuem valor EnumMember não foi considerado.Aqui vamos nos:

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

E exemplo desse enum:

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

Você deve usar Enum.Parse para obter o valor do objeto de Enum, depois disso você deve alterar o valor do objeto para um valor enum específico.A conversão para o valor enum pode ser feita usando Convert.ChangeType.Por favor, dê uma olhada no seguinte trecho de código

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

Experimente este exemplo:

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

Neste exemplo você pode enviar cada string e definir seu Enum.Se seu Enum tinha os dados que você queria, retorne-os como seu Enum tipo.

        <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);}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top