Domanda

Qual è il modo migliore per convertire una stringa in un valore di enumerazione in C#?

Ho un tag di selezione HTML contenente i valori di un'enumerazione.Quando la pagina viene pubblicata, voglio prelevare il valore (che sarà sotto forma di stringa) e convertirlo nel valore di enumerazione.

In un mondo ideale, potrei fare qualcosa del genere:

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

ma non è un codice valido.

È stato utile?

Soluzione

In .NET Core e .NET >4 esiste un metodo di analisi generico:

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

Ciò include anche il nuovo inline di C#7 out variabili, quindi esegue il try-parse, la conversione nel tipo enum esplicito e inizializza+popola il file myStatus variabile.

Se hai accesso a C#7 e all'ultima versione di .NET, questo è il modo migliore.

Risposta originale

In .NET è piuttosto brutto (fino alla versione 4 o superiore):

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

Tendo a semplificarlo con:

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

Quindi posso fare:

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

Un'opzione suggerita nei commenti è aggiungere un'estensione, il che è abbastanza semplice:

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

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

Infine, potresti voler avere un'enumerazione predefinita da utilizzare se la stringa non può essere analizzata:

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

Il che rende questa la chiamata:

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

Tuttavia, farei attenzione ad aggiungere un metodo di estensione come questo a string poiché (senza controllo dello spazio dei nomi) apparirà su tutte le istanze di string se detengono un'enumerazione o meno (so 1234.ToString().ToEnum(StatusEnum.None) sarebbe valido ma privo di senso).Spesso è meglio evitare di ingombrare le classi principali di Microsoft con metodi aggiuntivi che si applicano solo in contesti molto specifici, a meno che l'intero team di sviluppo non abbia un'ottima conoscenza di ciò che fanno tali estensioni.

Altri suggerimenti

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

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

Può essere ulteriormente semplificato con C# 7.0 tipo di parametro incorporato:

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

Tieni presente che le prestazioni di Enum.Parse() sono pessime, perché è implementato tramite riflessione.(Lo stesso vale per Enum.ToString, che va nella direzione opposta.)

Se è necessario convertire stringhe in enumerazioni in codice sensibile alle prestazioni, la soluzione migliore è creare un file Dictionary<String,YourEnum> all'avvio e utilizzalo per eseguire le conversioni.

Stai cercando Enum.Parse.

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

Puoi usare metodi di estensione Ora:

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

E puoi chiamarli con il codice seguente (qui, FilterType è un tipo enum):

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

Quindi se avessi un'enumerazione chiamata mood sarebbe simile a questa:

   enum Mood
   {
      Angry,
      Happy,
      Sad
   } 

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

ATTENZIONE:

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

Enum.(Try)Parse() accetta più argomenti separati da virgole e li combina con il binario "o" |.Non puoi disabilitarlo e secondo me non lo vorrai quasi mai.

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

Anche se Three non è stato definito, x otterrebbe comunque valore int 3.E' anche peggio:Enum.Parse() può darti un valore che non è nemmeno definito per l'enum!

Non vorrei sperimentare le conseguenze degli utenti, volontariamente o meno, che innescano questo comportamento.

Inoltre, come menzionato da altri, le prestazioni non sono ideali per enumerazioni di grandi dimensioni, ovvero lineari nel numero di valori possibili.

Suggerisco quanto segue:

    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 È tuo amico:

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

Puoi estendere la risposta accettata con un valore predefinito per evitare eccezioni:

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

Poi lo chiami così:

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

Non abbiamo potuto assumere un input perfettamente valido e abbiamo optato per questa variazione della risposta di @ 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);
}

Analizza la stringa in TEnum senza try/catch e senza il metodo TryParse() da .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;
}

Codice semplicissimo utilizzando TryParse:

var value = "Active";

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

Mi piace la soluzione del metodo di estensione..

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

Di seguito la mia implementazione con test.

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

Ho usato class (versione fortemente tipizzata di Enum con analisi e miglioramenti delle prestazioni).L'ho trovato su GitHub e dovrebbe funzionare anche per .NET 3.5.Ha un sovraccarico di memoria poiché memorizza nel buffer un dizionario.

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

Il post sul blog è Enumerazioni: migliore sintassi, prestazioni migliorate e TryParse in NET 3.5.

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

Per le prestazioni questo potrebbe aiutare:

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

Ho scoperto che qui il caso con valori enum che hanno valore EnumMember non è stato considerato.Quindi eccoci qua:

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

Ed esempio di quell'enumerazione:

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

Devi utilizzare Enum.Parse per ottenere il valore dell'oggetto da Enum, dopodiché devi modificare il valore dell'oggetto in un valore enum specifico.Il cast del valore enum può essere eseguito utilizzando Convert.ChangeType.Si prega di dare un'occhiata al seguente snippet di codice

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

Prova questo esempio:

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

In questo esempio puoi inviare ogni stringa e impostare il tuo Enum.Se tuo Enum aveva i dati che volevi, restituiscili come tuoi 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);}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top