首页 文章

我应该如何将字符串转换为C#中的枚举?

提问于
浏览
615

在C#中将字符串转换为枚举值的最佳方法是什么?

我有一个包含枚举值的HTML选择标记 . 当页面发布时,我想要获取值(将以字符串的形式)并将其转换为枚举值 .

在一个理想的世界里,我可以这样做:

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

但那不是有效的代码 .

20 回答

  • 3

    BEWARE:

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

    Enum.(Try)Parse() accepts multiple, comma-separated arguments, and combines them with binary 'or' | . 你不能禁用它,在我看来你几乎从不想要它 .

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

    即使未定义 Threex 仍将获得int值 3 . 更糟糕的是:Enum.Parse()可以为你提供一个甚至没有为枚举定义的值!

    我不想让用户自愿或不情愿地触发这种行为 .

    另外,正如其他人所提到的,对于大型枚举,性能不太理想,即可能值的数量是线性的 .

    我建议如下:

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

    在没有try / catch和.NET 4.5的TryParse()方法的情况下,将字符串解析为TEnum

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

    请注意,Enum.Parse()的性能很糟糕,因为它是通过反射实现的 . (对于Enum.ToString也是如此,这是另一种方式 . )

    如果您需要在性能敏感的代码中将字符串转换为枚举,最好的办法是在启动时创建 Dictionary<String,YourEnum> 并使用它来进行转换 .

  • 15

    使用Enum.TryParse<T>(String, T)(≥.NET4.0):

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

    使用C#7.0的parameter type inlining可以进一步简化它:

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

    我喜欢扩展方法解决方案..

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

    下面是我的测试实现 .

    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
            }
    
  • 5

    对于性能,这可能有所帮助

    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];
        }
    
  • 2
    object Enum.Parse(System.Type enumType, string value, bool ignoreCase);
    

    所以,如果你有一个名为heart的枚举,它将如下所示:

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

    你在找Enum.Parse .

    SomeEnum enum = (SomeEnum)Enum.Parse(typeof(SomeEnum), "EnumValue");
    
  • 164
    // str.ToEnum<EnumType>()
    T static ToEnum<T>(this string str) 
    { 
        return (T) Enum.Parse(typeof(T), str);
    }
    
  • 9

    在.NET Core和.NET中> 4 there is a generic parse method

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

    这还包括C#7的新内联 out 变量,因此这将执行try-parse,转换为显式枚举类型并初始化填充 myStatus 变量 .

    如果您可以访问C#7和最新的.NET,这是最好的方法 .

    原始答案

    在.NET中它相当丑陋(直到4或以上):

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

    我倾向于简化这个:

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

    然后我可以做:

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

    评论中建议的一个选项是添加一个扩展,这很简单:

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

    最后,如果无法解析字符串,您可能希望使用默认枚举:

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

    这使得这个电话:

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

    但是,我会小心地将这样的扩展方法添加到 string 中(没有命名空间控制)它将出现在 string 的所有实例上,无论它们是否包含枚举(因此 1234.ToString().ToEnum(StatusEnum.None) 将是有效但无意义的) . 它是's often be best to avoid cluttering Microsoft'的核心类,其中包含仅适用于非常特定的上下文的额外方法,除非您的整个开发团队非常了解这些扩展的作用 .

  • 1

    您现在可以使用extension methods

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

    您可以通过以下代码调用它们(此处, FilterType 是枚举类型):

    FilterType filterType = type.ToEnum<FilterType>();
    
  • 256
    public static T ParseEnum<T>(string value)            //function declaration  
    {
        return (T) Enum.Parse(typeof(T), value);
    }
    
    Importance imp = EnumUtil.ParseEnum<Importance>("Active");   //function call
    

    ====================一个完整的程序====================

    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.
    
  • 14

    Enum.Parse是你的朋友:

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

    使用TryParse的超级简单代码:

    var value = "Active";
    
    StatusEnum status;
    if (!Enum.TryParse<StatusEnum>(value, out status))
        status = StatusEnum.Unknown;
    
  • 23

    我发现这里没有考虑具有EnumMember值的枚举值的情况 . 所以我们走了:

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

    这个枚举的例子:

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

    您可以使用默认值扩展接受的答案以避免例外:

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

    然后你称之为:

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

    我使用了类(Enum的强类型版本,具有解析和性能改进) . 我在GitHub上找到它,它也适用于.NET 3.5 . 它有一些内存开销,因为它缓冲字典 .

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

    博文是Enums – Better syntax, improved performance and TryParse in NET 3.5 .

    和代码:https://github.com/damieng/DamienGKit/blob/master/CSharp/DamienG.Library/System/EnumT.cs

  • 1

    我们无法假设完全有效的输入,并且随着@ 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;
    }
    
  • 1

    您必须使用Enum.Parse从Enum获取对象值,之后您必须将对象值更改为特定的枚举值 . 可以使用Convert.ChangeType来转换为枚举值 . 请查看以下代码段

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

    试试这个样本:

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

    在此示例中,您可以发送每个字符串,并设置 Enum . 如果 Enum 拥有您想要的数据,请将其作为 Enum 类型返回 .

相关问题