提问人:Ben Mills 提问时间:8/19/2008 最后编辑:David KlempfnerBen Mills 更新时间:1/11/2023 访问量:1182113
在 C 中将字符串转换为枚举#
Convert a string to an enum in C#
问:
在 C# 中将字符串转换为枚举值的最佳方法是什么?
我有一个包含枚举值的 HTML select 标签。当页面发布时,我想获取值(将采用字符串的形式)并将其转换为相应的枚举值。
在一个理想的世界里,我可以做这样的事情:
StatusEnum MyStatus = StatusEnum.Parse("Active");
但这不是有效的代码。
答:
您正在寻找 Enum.Parse。
SomeEnum enum = (SomeEnum)Enum.Parse(typeof(SomeEnum), "EnumValue");
评论
在 .NET Core 和 .NET Framework ≥4.0 中,有一个通用的分析方法:
Enum.TryParse("Active", out StatusEnum myStatus);
这还包括 C#7 的新内联变量,因此这会执行 try-parse、转换为显式枚举类型并初始化 + 填充变量。out
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);
但是,我会小心地将这样的扩展方法添加到(没有命名空间控制)中,无论它们是否持有枚举,它都会出现在所有实例上(因此是有效的,但毫无意义)。通常最好避免使用仅适用于非常特定上下文的额外方法混淆 Microsoft 的核心类,除非您的整个开发团队非常了解这些扩展的功能。string
string
1234.ToString().ToEnum(StatusEnum.None)
评论
Enum.Parse 是你的朋友:
StatusEnum MyStatus = (StatusEnum)Enum.Parse(typeof(StatusEnum), "Active");
object Enum.Parse(System.Type enumType, string value, bool ignoreCase);
因此,如果你有一个名为 mood 的枚举,它将如下所示:
enum Mood
{
Angry,
Happy,
Sad
}
// ...
Mood m = (Mood) Enum.Parse(typeof(Mood), "Happy", true);
Console.WriteLine("My mood is: {0}", m.ToString());
// str.ToEnum<EnumType>()
T static ToEnum<T>(this string str)
{
return (T) Enum.Parse(typeof(T), str);
}
请注意,性能并不理想,因为它是通过反射实现的。(反之亦然。Enum.Parse()
Enum.ToString()
如果您需要在性能敏感的代码中将字符串转换为枚举,最好的办法是在启动时创建一个,并使用它来进行转换。Dictionary<String,YourEnum>
评论
MyEnum myEnum = Enum.Parse<MyEnum>("Seven");
Enum.TryParse<MyEnum>(Input, out MyEnum result);
我们不能假设输入完全有效,于是采用了@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;
}
将字符串解析为 TEnum,而无需 try/catch 和 .NET 4.5 中的 TryParse() 方法
/// <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;
}
评论
使用 Enum.TryParse<T>(String, T) (≥ .NET 4.0
):
StatusEnum myStatus;
Enum.TryParse("Active", out myStatus);
使用 C# 7.0 的参数类型内联可以进一步简化它:
Enum.TryParse("Active", out StatusEnum myStatus);
评论
Parse
null
TryParse
var result = Enum.TryParse<System.DayOfWeek>("55", out var parsedEnum);
&& Enum.IsDefined(typeof(System.DayOfWeek), parsedEnum)
您现在可以使用扩展方法:
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>();
评论
您可以使用默认值扩展接受的答案以避免异常:
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);
如果默认值不是枚举,则 Enum.TryParse 将失败并引发捕获的异常。
在许多地方的代码中使用此函数多年后,也许最好添加此操作需要性能的信息!
评论
defaultValue
T
我使用了类(具有解析和性能改进的 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
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.
我喜欢扩展方法解决方案..
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
}
小心:
enum Example
{
One = 1,
Two = 2,
Three = 3
}
Enum.(Try)Parse()
接受多个逗号分隔的参数,并将它们与二进制 'or' |
.你不能禁用它,在我看来,你几乎从不想要它。
var x = Enum.Parse("One,Two"); // x is now Three
即使没有定义,仍然会得到 int 值。更糟糕的是:Enum.Parse() 可以给你一个甚至没有为枚举定义的值!Three
x
3
我不想经历用户自愿或不愿意触发此行为的后果。
此外,正如其他人所提到的,对于大型枚举(即可能值的数量呈线性)而言,性能并不理想。
我提出以下建议:
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.(Try)Parse accepts multiple, comma-separated arguments, and combines them with binary 'or'
为了提高性能,这可能会有所帮助:
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];
}
评论
我发现这里没有考虑具有 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
}
使用 TryParse 的超级简单代码:
var value = "Active";
StatusEnum status;
if (!Enum.TryParse<StatusEnum>(value, out status))
status = StatusEnum.Unknown;
您必须使用 Enum.Parse 从 Enum 中获取对象值,然后您必须将对象值更改为特定的枚举值。可以使用 Convert.ChangeType 强制转换为枚举值。请查看以下代码片段
public T ConvertStringValueToEnum<T>(string valueToParse){
return Convert.ChangeType(Enum.Parse(typeof(T), valueToParse, true), typeof(T));
}
试试这个例子:
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
评论
newModel
Replace
var newModel = model.Replace("-", "").Replace(" ", "");
<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);}
不确定这是什么时候添加的,但在 Enum 类上现在有一个
Parse<TEnum>(stringValue)
与相关示例一起使用:
var MyStatus = Enum.Parse<StatusEnum >("Active")
或通过以下方式忽略大小写:
var MyStatus = Enum.Parse<StatusEnum >("active", true)
以下是它使用的反编译方法:
[NullableContext(0)]
public static TEnum Parse<TEnum>([Nullable(1)] string value) where TEnum : struct
{
return Enum.Parse<TEnum>(value, false);
}
[NullableContext(0)]
public static TEnum Parse<TEnum>([Nullable(1)] string value, bool ignoreCase) where TEnum : struct
{
TEnum result;
Enum.TryParse<TEnum>(value, ignoreCase, true, out result);
return result;
}
评论
如果属性名称与要调用的名称不同(即语言差异),则可以这样做:
MyType.cs
using System;
using System.Runtime.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
[JsonConverter(typeof(StringEnumConverter))]
public enum MyType
{
[EnumMember(Value = "person")]
Person,
[EnumMember(Value = "annan_deltagare")]
OtherPerson,
[EnumMember(Value = "regel")]
Rule,
}
EnumExtensions.cs
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public static class EnumExtensions
{
public static TEnum ToEnum<TEnum>(this string value) where TEnum : Enum
{
var jsonString = $"'{value.ToLower()}'";
return JsonConvert.DeserializeObject<TEnum>(jsonString, new StringEnumConverter());
}
public static bool EqualsTo<TEnum>(this string strA, TEnum enumB) where TEnum : Enum
{
TEnum enumA;
try
{
enumA = strA.ToEnum<TEnum>();
}
catch
{
return false;
}
return enumA.Equals(enumB);
}
}
程序.cs
public class Program
{
static public void Main(String[] args)
{
var myString = "annan_deltagare";
var myType = myString.ToEnum<MyType>();
var isEqual = myString.EqualsTo(MyType.OtherPerson);
//Output: true
}
}
如果要在 null 或空时使用默认值(例如,从配置文件检索且该值不存在时),并在字符串或数字与任何枚举值不匹配时引发异常。不过,要注意Timo的回答中的警告(https://stackoverflow.com/a/34267134/2454604)。
public static T ParseEnum<T>(this string s, T defaultValue, bool ignoreCase = false)
where T : struct, IComparable, IConvertible, IFormattable//If C# >=7.3: struct, System.Enum
{
if ((s?.Length ?? 0) == 0)
{
return defaultValue;
}
var valid = Enum.TryParse<T>(s, ignoreCase, out T res);
if (!valid || !Enum.IsDefined(typeof(T), res))
{
throw new InvalidOperationException(
$"'{s}' is not a valid value of enum '{typeof(T).FullName}'!");
}
return res;
}
首先,你需要装饰你的枚举,如下所示:
public enum Store : short
{
[Description("Rio Big Store")]
Rio = 1
}
在 .NET 5 中,我创建了以下扩展方法:
//The class also needs to be static, ok?
public static string GetDescription(this System.Enum enumValue)
{
FieldInfo fi = enumValue.GetType().GetField(enumValue.ToString());
DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(
typeof(DescriptionAttribute), false);
if (attributes != null && attributes.Length > 0) return attributes[0].Description;
else return enumValue.ToString();
}
现在,您有了一个可以在任何枚举中使用的扩展方法
喜欢这个:
var Desc = Store.Rio.GetDescription(); //Store is your Enum
评论
在某个时候添加了 Parse 的通用版本。对我来说,这是可取的,因为我不需要“尝试”解析,而且我也希望在不生成输出变量的情况下内联结果。
ColorEnum color = Enum.Parse<ColorEnum>("blue");
评论
ColorEnum color = (ColorEnum)Enum.Parse(typeof(ColorEnum), "blue");
此处的大多数答案都要求每次调用扩展方法时始终传入枚举的默认值。如果您不想采用这种方法,可以按如下方式实现它:
public static TEnum ToEnum<TEnum>(this string value) where TEnum : struct
{
if (string.IsNullOrWhiteSpace(value))
return default(TEnum);
return Enum.TryParse(value, true, out TEnum result) ? result : default(TEnum);
}
使用默认文本(从 C# 7.1 中提供)
public static TEnum ToEnum<TEnum>(this string value, TEnum defaultValue = default) where TEnum : struct
{
if (string.IsNullOrWhiteSpace(value))
return defaultValue ;
return Enum.TryParse(value, true, out TEnum result) ? result : defaultValue ;
}
更好的是:
public static TEnum ToEnum<TEnum>(this string value) where TEnum : struct
{
if (string.IsNullOrWhiteSpace(value))
return default;
return Enum.TryParse(value, true, out TEnum result) ? result : default;
}
评论
default
defaultValue
return default
: default
return defaultValue
: default
IDE0060 - Remove unused parameter ‘defaultValue’ if it is not part of a shipped public API
where T : System.Enum
where T : struct, System.Enum
default
defaultValue
return default
return defaultValue
TryParse(...) ? result : defaultValue;
我开始使用这种方法。性能似乎还可以,但是它需要一些样板代码设置。
public enum StatusType {
Success,
Pending,
Rejected
}
static class StatusTypeMethods {
public static StatusType GetEnum(string type) {
switch (type) {
case nameof(StatusType.Success): return StatusType.Success;
case nameof(StatusType.Pending): return StatusType.Pending;
case nameof(StatusType.Rejected): return StatusType.Rejected;
default:
throw new ArgumentOutOfRangeException(nameof(type), type, null);
};
}
}
稍后,您可以像这样使用它:
StatusType = StatusType.GetEnum("Success");
评论