枚举的字符串表示

我有以下枚举:

public enum AuthenticationMethod{FORMS = 1,WINDOWSAUTHENTICATION = 2,SINGLESIGNON = 3}

然而,问题是,当我要求AuthenticationMethod.FORMS而不是id 1时,我需要“表格”这个词。

我找到了这个问题的以下解决方案(链接):

首先,我需要创建一个名为“StringValue”的自定义属性:

public class StringValue : System.Attribute{private readonly string _value;
public StringValue(string value){_value = value;}
public string Value{get { return _value; }}
}

然后我可以将此属性添加到我的枚举器:

public enum AuthenticationMethod{[StringValue("FORMS")]FORMS = 1,[StringValue("WINDOWS")]WINDOWSAUTHENTICATION = 2,[StringValue("SSO")]SINGLESIGNON = 3}

当然,我需要一些东西来检索StringValue:

public static class StringEnum{public static string GetStringValue(Enum value){string output = null;Type type = value.GetType();
//Check first in our cached results...
//Look for our 'StringValueAttribute'
//in the field's custom attributes
FieldInfo fi = type.GetField(value.ToString());StringValue[] attrs =fi.GetCustomAttributes(typeof(StringValue),false) as StringValue[];if (attrs.Length > 0){output = attrs[0].Value;}
return output;}}

很好,现在我有了获取枚举器字符串值的工具。我可以这样使用它:

string valueOfAuthenticationMethod = StringEnum.GetStringValue(AuthenticationMethod.FORMS);

好的,现在所有这些工作都像一个魅力,但我发现它有很多工作。我想知道是否有更好的解决方案。

我还尝试了一些带有字典和静态属性的东西,但那也不是更好。

817593 次浏览

使用方法

Enum.GetName(Type MyEnumType,  object enumvariable)

例如(假设Shipper是一个定义的枚举)

Shipper x = Shipper.FederalExpress;string s = Enum.GetName(typeof(Shipper), x);

Enum类上还有一堆其他静态方法也值得研究……

不幸的是,在枚举上获取属性的反射非常慢:

看这个问题:谁知道一种快速获取枚举值自定义属性的方法?

.ToString()在枚举上也很慢。

不过,您可以为枚举编写扩展方法:

public static string GetName( this MyEnum input ) {switch ( input ) {case MyEnum.WINDOWSAUTHENTICATION:return "Windows";//and so on}}

这不是很好,但会很快,不需要反射属性或字段名称。


C#6更新

如果您可以使用C#6,那么新的nameof运算符适用于枚举,因此nameof(MyEnum.WINDOWSAUTHENTICATION)将在编译时间处转换为"WINDOWSAUTHENTICATION",使其成为获取枚举名称的最快方法。

请注意,这将把显式枚举转换为内联常量,因此它不适用于变量中的枚举。所以:

nameof(AuthenticationMethod.FORMS) == "FORMS"

可是…

var myMethod = AuthenticationMethod.FORMS;nameof(myMethod) == "myMethod"

您可以使用ToString()引用名称而不是值

Console.WriteLine("Auth method: {0}", AuthenticationMethod.Forms.ToString());

留档在这里:

http://msdn.microsoft.com/en-us/library/16c1xs4z.aspx

…如果您在Pascal Case中命名枚举(就像我一样-例如ThisIsMyEnumValue=1等),那么您可以使用一个非常简单的正则表达式来打印友好的表单:

static string ToFriendlyCase(this string EnumString){return Regex.Replace(EnumString, "(?!^)([A-Z])", " $1");}

它可以很容易地从任何字符串调用:

Console.WriteLine("ConvertMyCrazyPascalCaseSentenceToFriendlyCase".ToFriendlyCase());

产出:

将我的疯狂帕斯卡案件句子转换为友好案件

这节省了在房子周围运行的所有方式,创建自定义属性并将它们附加到您的枚举,或者使用查找表将枚举值与友好的字符串结合起来,最重要的是它是自我管理的,并且可以在任何可重用得多的Pascal Case字符串上使用。当然,它不允许您拥有比您的解决方案提供的枚举不同友好的名称。

不过,对于更复杂的场景,我确实喜欢你的原始解决方案。你可以更进一步,使你的GetStringValue成为枚举的扩展方法,然后你就不需要像StringEnumm. GetStringValue…

public static string GetStringValue(this AuthenticationMethod value){string output = null;Type type = value.GetType();FieldInfo fi = type.GetField(value.ToString());StringValue[] attrs = fi.GetCustomAttributes(typeof(StringValue), false) as StringValue[];if (attrs.Length > 0)output = attrs[0].Value;return output;}

然后,您可以直接从枚举实例轻松访问它:

Console.WriteLine(AuthenticationMethod.SSO.GetStringValue());

尝试类型安全枚举模式。

public sealed class AuthenticationMethod {
private readonly String name;private readonly int value;
public static readonly AuthenticationMethod FORMS = new AuthenticationMethod (1, "FORMS");public static readonly AuthenticationMethod WINDOWSAUTHENTICATION = new AuthenticationMethod (2, "WINDOWS");public static readonly AuthenticationMethod SINGLESIGNON = new AuthenticationMethod (3, "SSN");
private AuthenticationMethod(int value, String name){this.name = name;this.value = value;}
public override String ToString(){return name;}
}

更新显式(或隐式)类型转换可以通过

完成
  • 添加带有映射的静态字段

    private static readonly Dictionary<string, AuthenticationMethod> instance = new Dictionary<string,AuthenticationMethod>();
    • 注意为了在调用实例构造函数时“枚举成员”字段的初始化不会抛出NullReReference ceException,请务必将字典字段放在类中的“枚举成员”字段之前。这是因为静态字段初始化器是按声明顺序调用的,在静态构造函数之前调用,从而产生了奇怪且必要但令人困惑的情况,即在所有静态字段初始化之前可以调用实例构造函数,并且在调用静态构造函数之前。
  • 在实例构造函数中填充此映射

    instance[name] = this;
  • and adding user-defined type conversion operator

    public static explicit operator AuthenticationMethod(string str){AuthenticationMethod result;if (instance.TryGetValue(str, out result))return result;elsethrow new InvalidCastException();}

我使用System. ComponentModel命名空间中的描述属性。只需装饰枚举,然后使用此代码检索它:

public static string GetDescription<T>(this object enumerationValue)where T : struct{Type type = enumerationValue.GetType();if (!type.IsEnum){throw new ArgumentException("EnumerationValue must be of Enum type", "enumerationValue");}
//Tries to find a DescriptionAttribute for a potential friendly name//for the enumMemberInfo[] memberInfo = type.GetMember(enumerationValue.ToString());if (memberInfo != null && memberInfo.Length > 0){object[] attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attrs != null && attrs.Length > 0){//Pull out the description valuereturn ((DescriptionAttribute)attrs[0]).Description;}}//If we have no description attribute, just return the ToString of the enumreturn enumerationValue.ToString();
}

举个例子:

public enum Cycle : int{[Description("Daily Cycle")]Daily = 1,Weekly,Monthly}

这段代码很好地迎合了不需要“友好名称”的枚举,并且只返回枚举的. ToString()。

我同意基思的观点,但我还不能投票。

我使用静态方法和swith语句来准确返回我想要的。在数据库中,我存储tinyint,我的代码只使用实际的枚举,所以字符串是针对UI需求的。经过多次测试,这导致了最佳性能和对输出的最大控制。

public static string ToSimpleString(this enum){switch (enum){case ComplexForms:return "ComplexForms";break;}}
public static string ToFormattedString(this enum){switch (enum){case ComplexForms:return "Complex Forms";break;}}

然而,根据某些说法,这可能会导致维护噩梦和一些代码异味。我试着留意那些长且大量枚举的枚举,或者那些经常更改的枚举。否则,这对我来说是一个很好的解决方案。

备选案文1:

public sealed class FormsAuth{public override string ToString{return "Forms Authtentication";}}public sealed class WindowsAuth{public override string ToString{return "Windows Authtentication";}}
public sealed class SsoAuth{public override string ToString{return "SSO";}}

然后

object auth = new SsoAuth(); //or whatever
//...//...// blablabla
DoSomethingWithTheAuth(auth.ToString());

备选案文2:

public enum AuthenticationMethod{FORMS = 1,WINDOWSAUTHENTICATION = 2,SINGLESIGNON = 3}
public class MyClass{private Dictionary<AuthenticationMethod, String> map = new Dictionary<AuthenticationMethod, String>();public MyClass(){map.Add(AuthenticationMethod.FORMS,"Forms Authentication");map.Add(AuthenticationMethod.WINDOWSAUTHENTICATION ,"Windows Authentication");map.Add(AuthenticationMethod.SINGLESIGNON ,"SSo Authentication");}}

我使用了上面几个建议的组合,结合了一些缓存。现在,我从我在网上某个地方找到的一些代码中得到了这个想法,但我既不记得我在哪里得到它,也不记得找到它。所以如果有人发现了看起来相似的东西,请评论归因。

无论如何,用法涉及类型转换器,因此如果您绑定到UI,它“正常工作”。您可以通过从类型转换器初始化为静态方法来扩展Jakub的模式以快速查找代码。

基本用法如下所示

[TypeConverter(typeof(CustomEnumTypeConverter<MyEnum>))]public enum MyEnum{// The custom type converter will use the description attribute[Description("A custom description")]ValueWithCustomDescription,
// This will be exposed exactly.Exact}

自定义枚举类型转换器的代码如下:

public class CustomEnumTypeConverter<T> : EnumConverterwhere T : struct{private static readonly Dictionary<T,string> s_toString =new Dictionary<T, string>();
private static readonly Dictionary<string, T> s_toValue =new Dictionary<string, T>();
private static bool s_isInitialized;
static CustomEnumTypeConverter(){System.Diagnostics.Debug.Assert(typeof(T).IsEnum,"The custom enum class must be used with an enum type.");}
public CustomEnumTypeConverter() : base(typeof(T)){if (!s_isInitialized){Initialize();s_isInitialized = true;}}
protected void Initialize(){foreach (T item in Enum.GetValues(typeof(T))){string description = GetDescription(item);s_toString[item] = description;s_toValue[description] = item;}}
private static string GetDescription(T optionValue){var optionDescription = optionValue.ToString();var optionInfo = typeof(T).GetField(optionDescription);if (Attribute.IsDefined(optionInfo, typeof(DescriptionAttribute))){var attribute =(DescriptionAttribute)Attribute.GetCustomAttribute(optionInfo, typeof(DescriptionAttribute));return attribute.Description;}return optionDescription;}
public override object ConvertTo(ITypeDescriptorContext context,System.Globalization.CultureInfo culture,object value, Type destinationType){var optionValue = (T)value;
if (destinationType == typeof(string) &&s_toString.ContainsKey(optionValue)){return s_toString[optionValue];}
return base.ConvertTo(context, culture, value, destinationType);}
public override object ConvertFrom(ITypeDescriptorContext context,System.Globalization.CultureInfo culture, object value){var stringValue = value as string;
if (!string.IsNullOrEmpty(stringValue) && s_toValue.ContainsKey(stringValue)){return s_toValue[stringValue];}
return base.ConvertFrom(context, culture, value);}}

}

当我面对这个问题时,有几个问题我首先试图找到答案:

  • 我的枚举值的名称是否足够友好,或者我是否需要提供更友好的名称?
  • 我需要往返吗?也就是说,我需要获取文本值并将它们解析为枚举值吗?
  • 这是我需要对项目中的多个枚举执行的操作,还是只需要一个枚举?
  • 我将用什么样的UI元素来呈现这些信息——特别是,我将绑定到UI,还是使用属性表?
  • 这是否需要本地化?

最简单的方法是使用Enum.GetValue(并使用Enum.Parse支持往返)。正如Steve Mitcham建议的那样,通常也值得构建TypeConverter来支持UI绑定。(当你使用属性表时,没有必要构建TypeConverter,这是属性表的好处之一。尽管上帝知道它们有自己的问题。)

一般来说,如果对上述问题的回答表明这是行不通的,我的下一步是创建和填充静态Dictionary<MyEnum, string>,或者可能是Dictionary<Type, Dictionary<int, string>>。我倾向于跳过中间的用属性装饰代码步骤,因为接下来通常需要在部署后更改友好值(通常,但不总是,因为本地化)。

如果你想想我们要解决的问题,它根本不是我们需要的枚举。我们需要一个允许一定数量的值相互关联的对象;换句话说,定义一个类。

JakubŠturc的类型安全枚举模式是我在这里看到的最佳选择。

看看它:

  • 它有一个私有构造函数,因此只有类本身可以定义允许的值。
  • 它是一个密封类,因此值不能通过继承进行修改。
  • 它是类型安全的,允许您的方法只需要该类型。
  • 访问这些值不会对反射性能造成影响。
  • 最后,可以修改它以将两个以上的字段关联在一起,例如名称、描述和数值。

基于MSDN:http://msdn.microsoft.com/en-us/library/cc138362.aspx

foreach (string str in Enum.GetNames(typeof(enumHeaderField))){Debug.WriteLine(str);}

str将是字段的名称

我想将此作为对下面引用的帖子的评论发布,但不能,因为我没有足够的代表。代码包含一个错误,我想向尝试使用此解决方案的人指出这一点:

[TypeConverter(typeof(CustomEnumTypeConverter(typeof(MyEnum))]public enum MyEnum{// The custom type converter will use the description attribute[Description("A custom description")]ValueWithCustomDescription,// This will be exposed exactly.Exact}

应该是

[TypeConverter(typeof(CustomEnumTypeConverter<MyEnum>))]public enum MyEnum{// The custom type converter will use the description attribute[Description("A custom description")]ValueWithCustomDescription,
// This will be exposed exactly.Exact}

使用Object Emum. Parse(System. Type enumType, string value, bool忽略案例);http://blogs.msdn.com/b/tims/archive/2004/04/02/106310.aspx得到它

我发现的Enums国际化或从各自的资源文件中获取Enums文本的方法是通过继承描述属性类来创建一个属性类

public class EnumResourceAttribute : DescriptionAttribute{
public Type ResourceType { get; private set; }public string ResourceName { get; private set; }public int SortOrder { get; private set; }public EnumResourceAttribute(Type ResourceType,string ResourceName,int SortOrder){
this.ResourceType = ResourceType;this.ResourceName = ResourceName;this.SortOrder = SortOrder;}}

创建另一个静态类,该类将为GetString和GetStrings提供扩展方法。

public static class EnumHelper{public static string GetString(this Enum value){EnumResourceAttribute ea =(EnumResourceAttribute)value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof(EnumResourceAttribute), false).FirstOrDefault();if (ea != null){PropertyInfo pi = ea.ResourceType.GetProperty(CommonConstants.ResourceManager);if (pi != null){ResourceManager rm = (ResourceManager)pi.GetValue(null, null);return rm.GetString(ea.ResourceName);}
}return string.Empty;}

public static IList GetStrings(this Type enumType){List<string> stringList = new List<string>();FieldInfo[] fiArray = enumType.GetFields();foreach (FieldInfo fi in fiArray){EnumResourceAttribute ea =(EnumResourceAttribute)fi.GetCustomAttributes(typeof(EnumResourceAttribute), false).FirstOrDefault();if (ea != null){PropertyInfo pi = ea.ResourceType.GetProperty(CommonConstants.ResourceManager);if (pi != null){ResourceManager rm = (ResourceManager)pi.GetValue(null, null);stringList.Add(rm.GetString(ea.ResourceName));}}}return stringList.ToList();}}

在Enum的元素上,你可以写:

public enum Priority{[EnumResourceAttribute(typeof(Resources.AdviceModule), Resources.ResourceNames.AdviceCreateAdviceExternaPriorityMemberHigh, 1)]High,[EnumResourceAttribute(typeof(Resources.AdviceModule), Resources.ResourceNames.AdviceCreateAdviceExternaPriorityMemberRoutine, 2)]Routine}

AdviceAdviceExternaPriorityMemberHigh&

获取资源的优先级Routine是资源文件中的常量,或者您可以说出其值可以在不同文化中可用的字符串。

如果您在MVC架构中实现您的Web应用程序,那么请创建一个属性

private IList result;public IList Result{get{result = typeof(Priority).GetStrings();return result;}}

在您的. cshtml文件中,您可以将枚举绑定到您的下拉列表,例如:

@Html.DropDownListFor(model => Model.vwClinicalInfo.Priority, new SelectList(Model.Result))

谢谢Ratnesh

更新时间:访问这个页面,8年后,在很长一段时间没有接触C#之后,看起来我的答案不再是最好的解决方案。我真的很喜欢与属性函数捆绑在一起的转换器解决方案。

如果您正在阅读本文,请确保您还查看了其他答案。
(提示:它们在这个上面)


和你们大多数人一样,我真的很喜欢所选的作者:JakubŠturc,但我也真的很讨厌复制粘贴代码,并尽量少做。

所以我决定我想要一个EnumBase类,其中大部分功能都是继承/内置的,让我专注于内容而不是行为。

这种方法的主要问题是基于这样一个事实,即尽管枚举值是类型安全的实例,但交互是与枚举类类型的静态实现。所以在泛型魔法的帮助下,我想我终于得到了正确的组合。希望有人能像我一样发现这个有用。

我将从Jakub的示例开始,但使用继承和泛型:

public sealed class AuthenticationMethod : EnumBase<AuthenticationMethod, int>{public static readonly AuthenticationMethod FORMS =new AuthenticationMethod(1, "FORMS");public static readonly AuthenticationMethod WINDOWSAUTHENTICATION =new AuthenticationMethod(2, "WINDOWS");public static readonly AuthenticationMethod SINGLESIGNON =new AuthenticationMethod(3, "SSN");
private AuthenticationMethod(int Value, String Name): base( Value, Name ) { }public new static IEnumerable<AuthenticationMethod> All{ get { return EnumBase<AuthenticationMethod, int>.All; } }public static explicit operator AuthenticationMethod(string str){ return Parse(str); }}

这是基类:

using System;using System.Collections.Generic;using System.Linq; // for the .AsEnumerable() method call
// E is the derived type-safe-enum class// - this allows all static members to be truly unique to the specific//   derived classpublic class EnumBase<E, T> where E: EnumBase<E, T>{#region Instance codepublic T Value { get; private set; }public string Name { get; private set; }
protected EnumBase(T EnumValue, string Name){Value = EnumValue;this.Name = Name;mapping.Add(Name, this);}
public override string ToString() { return Name; }#endregion
#region Static toolsstatic private readonly Dictionary<string, EnumBase<E, T>> mapping;static EnumBase() { mapping = new Dictionary<string, EnumBase<E, T>>(); }protected static E Parse(string name){EnumBase<E, T> result;if (mapping.TryGetValue(name, out result)){return (E)result;}
throw new InvalidCastException();}// This is protected to force the child class to expose it's own static// method.// By recreating this static method at the derived class, static// initialization will be explicit, promising the mapping dictionary// will never be empty when this method is called.protected static IEnumerable<E> All{ get { return mapping.Values.AsEnumerable().Cast<E>(); } }#endregion}

我的变种

public struct Colors{private String current;
private static string red = "#ff0000";private static string green = "#00ff00";private static string blue = "#0000ff";
private static IList<String> possibleColors;
public static Colors Red { get { return (Colors) red; } }public static Colors Green { get { return (Colors) green; } }public static Colors Blue { get { return (Colors) blue; } }
static Colors(){possibleColors = new List<string>() {red, green, blue};}
public static explicit operator String(Colors value){return value.current;}
public static explicit operator Colors(String value){if (!possibleColors.Contains(value)){throw new InvalidCastException();}
Colors color = new Colors();color.current = value;return color;}
public static bool operator ==(Colors left, Colors right){return left.current == right.current;}
public static bool operator !=(Colors left, Colors right){return left.current != right.current;}
public bool Equals(Colors other){return Equals(other.current, current);}
public override bool Equals(object obj){if (ReferenceEquals(null, obj)) return false;if (obj.GetType() != typeof(Colors)) return false;return Equals((Colors)obj);}
public override int GetHashCode(){return (current != null ? current.GetHashCode() : 0);}
public override string ToString(){return current;}}

代码看起来有点难看,但是这个结构的用法非常有表现力。

Colors color1 = Colors.Red;Console.WriteLine(color1); // #ff0000
Colors color2 = (Colors) "#00ff00";Console.WriteLine(color2); // #00ff00
// Colors color3 = "#0000ff"; // Compilation error// String color4 = Colors.Red; // Compilation error
Colors color5 = (Colors)"#ff0000";Console.WriteLine(color1 == color5); // True
Colors color6 = (Colors)"#00ff00";Console.WriteLine(color1 == color6); // False

此外,我认为,如果需要很多这样的枚举,可能会使用代码生成(例如T4)。

我使用扩展方法:

public static class AttributesHelperExtension{public static string ToDescription(this Enum value){var da = (DescriptionAttribute[])(value.GetType().GetField(value.ToString())).GetCustomAttributes(typeof(DescriptionAttribute), false);return da.Length > 0 ? da[0].Description : value.ToString();}}

现在用enum装饰:

public enum AuthenticationMethod{[Description("FORMS")]FORMS = 1,[Description("WINDOWSAUTHENTICATION")]WINDOWSAUTHENTICATION = 2,[Description("SINGLESIGNON ")]SINGLESIGNON = 3}

当你呼唤

AuthenticationMethod.FORMS.ToDescription()你会得到"FORMS"

如果你来这里是为了实现一个简单的“枚举”,但其值是字符串而不是整数,这里是最简单的解决方案:

    public sealed class MetricValueList{public static readonly string Brand = "A4082457-D467-E111-98DC-0026B9010912";public static readonly string Name = "B5B5E167-D467-E111-98DC-0026B9010912";}

执行:

var someStringVariable = MetricValueList.Brand;

我如何将其作为扩展方法解决:

using System.ComponentModel;public static string GetDescription(this Enum value){var descriptionAttribute = (DescriptionAttribute)value.GetType().GetField(value.ToString()).GetCustomAttributes(false).Where(a => a is DescriptionAttribute).FirstOrDefault();
return descriptionAttribute != null ? descriptionAttribute.Description : value.ToString();}

枚举:

public enum OrderType{None = 0,[Description("New Card")]NewCard = 1,[Description("Reload")]Refill = 2}

用法(其中o. OrderType是与枚举同名的属性):

o.OrderType.GetDescription()

这给了我一个字符串“New Card”或“Reload”,而不是实际的枚举值NewCard和Re填充。

我的答案,使用@user29964的答案(这是迄今为止最简单和最接近枚举的答案)是

 public class StringValue : System.Attribute{private string _value;
public StringValue(string value){_value = value;}
public string Value{get { return _value; }}


public static string GetStringValue(Enum Flagvalue){Type type = Flagvalue.GetType();string[] flags = Flagvalue.ToString().Split(',').Select(x => x.Trim()).ToArray();List<string> values = new List<string>();
for (int i = 0; i < flags.Length; i++){
FieldInfo fi = type.GetField(flags[i].ToString());
StringValue[] attrs =fi.GetCustomAttributes(typeof(StringValue),false) as StringValue[];if (attrs.Length > 0){values.Add(attrs[0].Value);}}return String.Join(",", values);
}

使用

[Flags]public enum CompeteMetric{
/// <summary>/// u/// </summary>[StringValue("u")]//Json mappingBasic_UniqueVisitors = 1 //Basic,/// <summary>/// vi/// </summary>[StringValue("vi")]//json mappingBasic_Visits = 2// Basic,/// <summary>/// rank/// </summary>[StringValue("rank")]//json mappingBasic_Rank = 4//Basic}

示例

        CompeteMetric metrics = CompeteMetric.Basic_Visits | CompeteMetric.Basic_Rank;string strmetrics = StringValue.GetStringValue(metrics);

这将返回"vi,等级"

使用ToString()方法

public enum any{Tomato=0,Melon,Watermelon}

要引用字符串Tomato,只需使用

any.Tomato.ToString();

对我来说,务实的方法是类中类,示例:

public class MSEModel{class WITS{public const string DATE = "5005";public const string TIME = "5006";public const string MD = "5008";public const string ROP = "5075";public const string WOB = "5073";public const string RPM = "7001";...}

嗯,在阅读了上述所有内容之后,我觉得这些家伙已经将枚举器转换为字符串的问题复杂化了。我喜欢在枚举字段上具有属性的想法,但我认为属性主要用于元数据,但在您的情况下,我认为您需要的只是某种本地化。

public enum Color{ Red = 1, Green = 2, Blue = 3}

public static EnumUtils{public static string GetEnumResourceString(object enumValue){Type enumType = enumValue.GetType();string value = Enum.GetName(enumValue.GetType(), enumValue);string resourceKey = String.Format("{0}_{1}", enumType.Name, value);string result = Resources.Enums.ResourceManager.GetString(resourceKey);if (string.IsNullOrEmpty(result)){result = String.Format("{0}", value);}return result;}}

现在如果我们尝试调用上面的方法我们可以这样调用它

public void Foo(){var col = Color.Red;Console.WriteLine (EnumUtils.GetEnumResourceString (col));}

您需要做的就是创建一个包含所有枚举值和相应字符串的资源文件

Resource Name          Resource ValueColor_Red              My String Color in RedColor_Blue             BlueeeyColor_Green            Hulk Color

实际上非常好的是,如果您需要将应用程序本地化,它将非常有帮助,因为您需要做的就是使用您的新语言创建另一个资源文件!和Voe-la!

这是另一种完成将字符串与枚举关联的任务的方法:

struct DATABASE {public enum enums {NOTCONNECTED, CONNECTED, ERROR}static List<string> strings =new List<string>() {"Not Connected", "Connected", "Error"};
public string GetString(DATABASE.enums value) {return strings[(int)value];}}

这个方法是这样调用的:

public FormMain() {DATABASE dbEnum;
string enumName = dbEnum.GetString(DATABASE.enums.NOTCONNECTED);}

您可以将相关枚举分组到它们自己的结构中。由于此方法使用枚举类型,因此您可以在进行GetString()调用时使用Intellisense显示枚举列表。

您可以选择在DATABASE结构上使用new运算符。不使用它意味着在进行第一次GetString()调用之前不会分配字符串List

我真的很喜欢JakubŠturc的答案,但它的缺点是你不能将它与Switch case语句一起使用。以下是他的答案的略微修改版本,可以与Switch语句一起使用:

public sealed class AuthenticationMethod{#region This code never needs to change.private readonly string _name;public readonly Values Value;
private AuthenticationMethod(Values value, String name){this._name = name;this.Value = value;}
public override String ToString(){return _name;}#endregion
public enum Values{Forms = 1,Windows = 2,SSN = 3}
public static readonly AuthenticationMethod FORMS = new AuthenticationMethod (Values.Forms, "FORMS");public static readonly AuthenticationMethod WINDOWSAUTHENTICATION = new AuthenticationMethod (Values.Windows, "WINDOWS");public static readonly AuthenticationMethod SINGLESIGNON = new AuthenticationMethod (Values.SSN, "SSN");}

因此,您可以获得JakubŠturc答案的所有好处,并且我们可以将其与Switch语句一起使用,如下所示:

var authenticationMethodVariable = AuthenticationMethod.FORMS;  // Set the "enum" value we want to use.var methodName = authenticationMethodVariable.ToString();       // Get the user-friendly "name" of the "enum" value.
// Perform logic based on which "enum" value was chosen.switch (authenticationMethodVariable.Value){case authenticationMethodVariable.Values.Forms: // Do somethingbreak;case authenticationMethodVariable.Values.Windows: // Do somethingbreak;case authenticationMethodVariable.Values.SSN: // Do somethingbreak;}

我和哈维在一起,但不使用const。我可以混合和匹配字符串、int等。

public class xlsLayout{public int xlHeaderRow = 1;public int xlFirstDataRow = 2;public int xlSkipLinesBetweenFiles = 1; //so 0 would mean don't skippublic string xlFileColumn = "A";public string xlFieldColumn = "B";public string xlFreindlyNameColumn = "C";public string xlHelpTextColumn = "D";}

后来…

public partial class Form1 : Form{xlsLayout xlLayout = new xlsLayout();
xl.SetCell(xlLayout.xlFileColumn, xlLayout.xlHeaderRow, "File Name");xl.SetCell(xlLayout.xlFieldColumn, xlLayout.xlHeaderRow, "Code field name");xl.SetCell(xlLayout.xlFreindlyNameColumn, xlLayout.xlHeaderRow, "Freindly name");xl.SetCell(xlLayout.xlHelpTextColumn, xlLayout.xlHeaderRow, "Inline Help Text");}

当我遇到这种情况时,我提出下面的解决方案。

作为一个消费阶层你可以

using System;using System.Collections.Generic;using System.Linq;using System.Text;
namespace MyApp.Dictionaries{class Greek{
public static readonly string Alpha = "Alpha";public static readonly string Beta = "Beta";public static readonly string Gamma = "Gamma";public static readonly string Delta = "Delta";

private static readonly BiDictionary<int, string> Dictionary = new BiDictionary<int, string>();

static Greek() {Dictionary.Add(1, Alpha);Dictionary.Add(2, Beta);Dictionary.Add(3, Gamma);Dictionary.Add(4, Delta);}
public static string getById(int id){return Dictionary.GetByFirst(id);}
public static int getByValue(string value){return Dictionary.GetBySecond(value);}
}}

并使用双向字典:基于此(https://stackoverflow.com/a/255638/986160)假设键将与字典中的单个值相关联,类似于(https://stackoverflow.com/a/255630/986160)但更优雅。此字典也是可枚举的,您可以在int和string之间来回切换。除了此类之外,您的代码库中不必有任何字符串。

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Collections;
namespace MyApp.Dictionaries{
class BiDictionary<TFirst, TSecond> : IEnumerable{IDictionary<TFirst, TSecond> firstToSecond = new Dictionary<TFirst, TSecond>();IDictionary<TSecond, TFirst> secondToFirst = new Dictionary<TSecond, TFirst>();
public void Add(TFirst first, TSecond second){firstToSecond.Add(first, second);secondToFirst.Add(second, first);}
public TSecond this[TFirst first]{get { return GetByFirst(first); }}
public TFirst this[TSecond second]{get { return GetBySecond(second); }}
public TSecond GetByFirst(TFirst first){return firstToSecond[first];}
public TFirst GetBySecond(TSecond second){return secondToFirst[second];}
public IEnumerator GetEnumerator(){return GetFirstEnumerator();}
public IEnumerator GetFirstEnumerator(){return firstToSecond.GetEnumerator();}
public IEnumerator GetSecondEnumerator(){return secondToFirst.GetEnumerator();}}}

这里有很多很好的答案,但在我的情况下并没有解决我想要的“字符串枚举”,即:

  1. 可用于Switch语句,例如Switch(myEnum)
  2. 可以在函数参数中使用,例如foo(myEnum类型)
  3. 可以引用,例如myEnumm. FirstElement
  4. 我可以使用字符串,例如foo("FirstElement")==foo(myEnumm. FirstElement)

1,2&4实际上可以用字符串的C#Typedef解决(因为字符串在c#中是可切换的)

3可以通过静态const字符串来解决。所以如果你有同样的需求,这是最简单的方法:

public sealed class Types{
private readonly String name;
private Types(String name){this.name = name;
}
public override String ToString(){return name;}
public static implicit operator Types(string str){return new Types(str);
}public static implicit operator string(Types str){return str.ToString();}

#region enum
public const string DataType = "Data";public const string ImageType = "Image";public const string Folder = "Folder";#endregion
}

这允许例如:

    public TypeArgs(Types SelectedType){Types SelectedType = SelectedType}

public TypeObject CreateType(Types type){switch (type){
case Types.ImageType://break;
case Types.DataType://break;
}}

其中CreateType可以用字符串或类型调用。但是缺点是任何字符串都会自动成为有效的枚举,这可以修改,但它需要某种初始化函数……或者可能使它们显式转换为内部?

现在,如果一个int值对你很重要(也许是为了比较速度),你可以使用JakubŠturc的一些想法,做一些疯狂的事情,这是我对它的尝试:

    public sealed class Types{private static readonly Dictionary<string, Types> strInstance = new Dictionary<string, Types>();private static readonly Dictionary<int, Types> intInstance = new Dictionary<int, Types>();
private readonly String name;private static int layerTypeCount = 0;private int value;private Types(String name){this.name = name;value = layerTypeCount++;strInstance[name] = this;intInstance[value] = this;}
public override String ToString(){return name;}

public static implicit operator Types(int val){Types result;if (intInstance.TryGetValue(val, out result))return result;elsethrow new InvalidCastException();}
public static implicit operator Types(string str){Types result;if (strInstance.TryGetValue(str, out result)){return result;}else{result = new Types(str);return result;}
}public static implicit operator string(Types str){return str.ToString();}
public static bool operator ==(Types a, Types b){return a.value == b.value;}public static bool operator !=(Types a, Types b){return a.value != b.value;}
#region enum
public const string DataType = "Data";public const string ImageType = "Image";
#endregion
}

但当然,“类型bob=4;”将毫无意义,除非你先初始化它们,这将在某种程度上击败这一点……

但理论上TypeA==TypeB会更快…

在你的问题中,你从来没有说过你在任何地方都需要枚举的数值。

如果您不需要并且只需要一个字符串类型的枚举(它不是整数类型,因此不能是枚举的基数),这里有一种方法:

    static class AuthenticationMethod{public static readonly stringFORMS = "Forms",WINDOWSAUTHENTICATION = "WindowsAuthentication";}

您可以使用与enum相同的语法来引用它

if (bla == AuthenticationMethod.FORMS)

它会比数值(比较字符串而不是数字)慢一点,但从好的方面来说,它不使用反射(慢)来访问字符串。

使用. Net 4.0及更高版本非常简单的解决方案。不需要其他代码。

public enum MyStatus{Active = 1,Archived = 2}

要获取有关的字符串,请使用:

MyStatus.Active.ToString("f");

MyStatus.Archived.ToString("f");`

该值将为“活动”或“存档”。

要在调用Enum.ToString时查看不同的字符串格式(上面的“f”),请参阅此枚举格式字符串页面

如果我理解正确,您可以简单地使用. ToString()从值中检索枚举的名称(假设它已经转换为枚举);如果您有裸int(假设来自数据库或其他东西),您可以首先将其转换为枚举。下面的两个方法都将为您提供枚举名称。

AuthenticationMethod myCurrentSetting = AuthenticationMethod.FORMS;Console.WriteLine(myCurrentSetting); // Prints: FORMSstring name = Enum.GetNames(typeof(AuthenticationMethod))[(int)myCurrentSetting-1];Console.WriteLine(name); // Prints: FORMS

不过请记住,第二种技术假设您使用的是int并且您的索引是基于1(而不是基于0)。相比之下,GetNames函数也相当重,每次调用它时您都会生成一个完整的数组。正如您在第一种技术中看到的,. ToString()实际上是隐式调用的。当然,这两个答案都已经提到了,我只是想澄清它们之间的区别。

老职位但…

这个问题的答案实际上可能非常简单。使用枚举. ToString()函数

这个函数有6个重载,您可以使用Enumm. Tostring(“F”)或Enumm. ToString()来返回字符串值。无需担心其他任何事情。这是一个工作演示

请注意,此解决方案可能不适用于所有编译器(此演示不按预期工作),但至少适用于最新的编译器。

我很清楚,这个问题已经有了答案,行动处也很满意这个公认的答案,但我发现大部分的答案,包括公认的答案,都有一点复杂。

我有一个项目给了我这样的情况,我能够以这种方式实现它。

首先,您必须考虑枚举名称的大小写:

public enum AuthenticationMethod{Forms = 1,WindowsAuthentication = 2,SingleSignOn = 3}

然后,有这个扩展:

using System.Text.RegularExpression;
public static class AnExtension{public static string Name(this Enum value){string strVal = value.ToString();try{return Regex.Replace(strVal, "([a-z])([A-Z])", "$1 $2");}catch{}return strVal;}}

通过这个,您可以将每个枚举名称转换为其字符串表示形式,每个单词用空格分隔。例如:

AuthenticationMethod am = AuthenticationMethod.WindowsAuthentication;MessageBox.Show(am.Name());

对于较大的字符串枚举集,列出的示例可能会变得令人厌倦。如果你想要一个状态代码列表,或者一个其他基于字符串的枚举列表,属性系统使用起来很烦人,配置一个具有自身实例的静态类也很烦人。对于我自己的解决方案,我使用T4模板来使字符串支持的枚举更容易。结果类似于HttpLaw类的工作方式。

你可以像这样使用它:

    string statusCode = ResponseStatusCode.SUCCESS; // Automatically converts to string when neededResponseStatusCode codeByValueOf = ResponseStatusCode.ValueOf(statusCode); // Returns null if not found
// Implements TypeConverter so you can use it with string conversion methods.var converter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(ResponseStatusCode));ResponseStatusCode code = (ResponseStatusCode) converter.ConvertFromInvariantString(statusCode);
// You can get a full list of the valuesbool canIterateOverValues = ResponseStatusCode.Values.Any();
// Comparisons are by value of the "Name" property. Not by memory pointer location.bool implementsByValueEqualsEqualsOperator = "SUCCESS" == ResponseStatusCode.SUCCESS;

你从一个Enum.tt文件开始。

<#@ include file="StringEnum.ttinclude" #>

<#+public static class Configuration{public static readonly string Namespace = "YourName.Space";public static readonly string EnumName = "ResponseStatusCode";public static readonly bool IncludeComments = true;
public static readonly object Nodes = new{SUCCESS = "The response was successful.",NON_SUCCESS = "The request was not successful.",RESOURCE_IS_DISCONTINUED = "The resource requested has been discontinued and can no longer be accessed."};}#>

然后,添加StringEnum.ttinclude文件。

<#@ template debug="false" hostspecific="false" language="C#" #><#@ assembly name="System.Core" #><#@ import namespace="System" #><#@ import namespace="System.Linq" #><#@ import namespace="System.Text" #><#@ import namespace="System.Reflection" #><#@ import namespace="System.Collections.Generic" #><#@ output extension=".cs" #><#@ CleanupBehavior processor="T4VSHost" CleanupAfterProcessingtemplate="true" #>
//------------------------------------------------------------------------------// <auto-generated>//     This code was generated by a tool.////     Changes to this file may cause incorrect behavior and will be lost if//     the code is regenerated.// </auto-generated>//------------------------------------------------------------------------------
using System;using System.Linq;using System.Collections.Generic;using System.ComponentModel;using System.Globalization;
namespace <#= Configuration.Namespace #>{/// <summary>/// TypeConverter implementations allow you to use features like string.ToNullable(T)./// </summary>public class <#= Configuration.EnumName #>TypeConverter : TypeConverter{public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType){return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value){var casted = value as string;
if (casted != null){var result = <#= Configuration.EnumName #>.ValueOf(casted);if (result != null){return result;}}
return base.ConvertFrom(context, culture, value);}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType){var casted = value as <#= Configuration.EnumName #>;if (casted != null && destinationType == typeof(string)){return casted.ToString();}
return base.ConvertTo(context, culture, value, destinationType);}}
[TypeConverter(typeof(<#= Configuration.EnumName #>TypeConverter))]public class <#= Configuration.EnumName #> : IEquatable<<#= Configuration.EnumName #>>{//---------------------------------------------------------------------------------------------------// V A L U E S _ L I S T//---------------------------------------------------------------------------------------------------<# Write(Helpers.PrintEnumProperties(Configuration.Nodes)); #>
private static List<<#= Configuration.EnumName #>> _list { get; set; } = null;public static List<<#= Configuration.EnumName #>> ToList(){if (_list == null){_list = typeof(<#= Configuration.EnumName #>).GetFields().Where(x => x.IsStatic && x.IsPublic && x.FieldType == typeof(<#= Configuration.EnumName #>)).Select(x => x.GetValue(null)).OfType<<#= Configuration.EnumName #>>().ToList();}
return _list;}
public static List<<#= Configuration.EnumName #>> Values(){return ToList();}
/// <summary>/// Returns the enum value based on the matching Name of the enum. Case-insensitive search./// </summary>/// <param name="key"></param>/// <returns></returns>public static <#= Configuration.EnumName #> ValueOf(string key){return ToList().FirstOrDefault(x => string.Compare(x.Name, key, true) == 0);}

//---------------------------------------------------------------------------------------------------// I N S T A N C E _ D E F I N I T I O N//---------------------------------------------------------------------------------------------------public string Name { get; private set; }public string Description { get; private set; }public override string ToString() { return this.Name; }
/// <summary>/// Implcitly converts to string./// </summary>/// <param name="d"></param>public static implicit operator string(<#= Configuration.EnumName #> d){return d.ToString();}
/// <summary>/// Compares based on the == method. Handles nulls gracefully./// </summary>/// <param name="a"></param>/// <param name="b"></param>/// <returns></returns>public static bool operator !=(<#= Configuration.EnumName #> a, <#= Configuration.EnumName #> b){return !(a == b);}
/// <summary>/// Compares based on the .Equals method. Handles nulls gracefully./// </summary>/// <param name="a"></param>/// <param name="b"></param>/// <returns></returns>public static bool operator ==(<#= Configuration.EnumName #> a, <#= Configuration.EnumName #> b){return a?.ToString() == b?.ToString();}
/// <summary>/// Compares based on the .ToString() method/// </summary>/// <param name="o"></param>/// <returns></returns>public override bool Equals(object o){return this.ToString() == o?.ToString();}
/// <summary>/// Compares based on the .ToString() method/// </summary>/// <param name="other"></param>/// <returns></returns>public bool Equals(<#= Configuration.EnumName #> other){return this.ToString() == other?.ToString();}
/// <summary>/// Compares based on the .Name property/// </summary>/// <returns></returns>public override int GetHashCode(){return this.Name.GetHashCode();}}}
<#+
public static class Helpers{public static string PrintEnumProperties(object nodes){string o = "";Type nodesTp = Configuration.Nodes.GetType();PropertyInfo[] props = nodesTp.GetProperties().OrderBy(p => p.Name).ToArray();
for(int i = 0; i < props.Length; i++){var prop = props[i];if (Configuration.IncludeComments){o += "\r\n\r\n";o += "\r\n        ///<summary>";o += "\r\n        /// "+Helpers.PrintPropertyValue(prop, Configuration.Nodes);o += "\r\n        ///</summary>";}
o += "\r\n        public static readonly "+Configuration.EnumName+" "+prop.Name+ " = new "+Configuration.EnumName+"(){ Name = \""+prop.Name+"\", Description = "+Helpers.PrintPropertyValue(prop, Configuration.Nodes)+ "};";}
o += "\r\n\r\n";
return o;}
private static Dictionary<string, string> GetValuesMap(){Type nodesTp = Configuration.Nodes.GetType();PropertyInfo[] props= nodesTp.GetProperties();var dic = new Dictionary<string,string>();for(int i = 0; i < props.Length; i++){var prop = nodesTp.GetProperties()[i];dic[prop.Name] = prop.GetValue(Configuration.Nodes).ToString();}return dic;}
public static string PrintMasterValuesMap(object nodes){Type nodesTp = Configuration.Nodes.GetType();PropertyInfo[] props= nodesTp.GetProperties();string o = "        private static readonly Dictionary<string, string> ValuesMap = new Dictionary<string, string>()\r\n        {";for(int i = 0; i < props.Length; i++){var prop = nodesTp.GetProperties()[i];o += "\r\n            { \""+prop.Name+"\", "+(Helpers.PrintPropertyValue(prop,Configuration.Nodes)+" },");}o += ("\r\n        };\r\n");
return o;}

public static string PrintPropertyValue(PropertyInfo prop, object objInstance){switch(prop.PropertyType.ToString()){case "System.Double":return prop.GetValue(objInstance).ToString()+"D";case "System.Float":return prop.GetValue(objInstance).ToString()+"F";case "System.Decimal":return prop.GetValue(objInstance).ToString()+"M";case "System.Long":return prop.GetValue(objInstance).ToString()+"L";case "System.Boolean":case "System.Int16":case "System.Int32":return prop.GetValue(objInstance).ToString().ToLowerInvariant();case "System.String":return "\""+prop.GetValue(objInstance)+"\"";}
return prop.GetValue(objInstance).ToString();}
public static string _ (int numSpaces){string o = "";for(int i = 0; i < numSpaces; i++){o += " ";}
return o;}}#>

最后,重新编译Enum.tt文件,输出如下所示:

//------------------------------------------------------------------------------// <auto-generated>//     This code was generated by a tool.////     Changes to this file may cause incorrect behavior and will be lost if//     the code is regenerated.// </auto-generated>//------------------------------------------------------------------------------
using System;using System.Linq;using System.Collections.Generic;
namespace YourName.Space{public class ResponseStatusCode{//---------------------------------------------------------------------------------------------------// V A L U E S _ L I S T//---------------------------------------------------------------------------------------------------


///<summary>/// "The response was successful."///</summary>public static readonly ResponseStatusCode SUCCESS = new ResponseStatusCode(){ Name = "SUCCESS", Description = "The response was successful."};

///<summary>/// "The request was not successful."///</summary>public static readonly ResponseStatusCode NON_SUCCESS = new ResponseStatusCode(){ Name = "NON_SUCCESS", Description = "The request was not successful."};

///<summary>/// "The resource requested has been discontinued and can no longer be accessed."///</summary>public static readonly ResponseStatusCode RESOURCE_IS_DISCONTINUED = new ResponseStatusCode(){ Name = "RESOURCE_IS_DISCONTINUED", Description = "The resource requested has been discontinued and can no longer be accessed."};

private static List<ResponseStatusCode> _list { get; set; } = null;public static List<ResponseStatusCode> ToList(){if (_list == null){_list = typeof(ResponseStatusCode).GetFields().Where(x => x.IsStatic && x.IsPublic && x.FieldType == typeof(ResponseStatusCode)).Select(x => x.GetValue(null)).OfType<ResponseStatusCode>().ToList();}
return _list;}
public static List<ResponseStatusCode> Values(){return ToList();}
/// <summary>/// Returns the enum value based on the matching Name of the enum. Case-insensitive search./// </summary>/// <param name="key"></param>/// <returns></returns>public static ResponseStatusCode ValueOf(string key){return ToList().FirstOrDefault(x => string.Compare(x.Name, key, true) == 0);}

//---------------------------------------------------------------------------------------------------// I N S T A N C E _ D E F I N I T I O N//---------------------------------------------------------------------------------------------------public string Name { get; set; }public string Description { get; set; }public override string ToString() { return this.Name; }
/// <summary>/// Implcitly converts to string./// </summary>/// <param name="d"></param>public static implicit operator string(ResponseStatusCode d){return d.ToString();}
/// <summary>/// Compares based on the == method. Handles nulls gracefully./// </summary>/// <param name="a"></param>/// <param name="b"></param>/// <returns></returns>public static bool operator !=(ResponseStatusCode a, ResponseStatusCode b){return !(a == b);}
/// <summary>/// Compares based on the .Equals method. Handles nulls gracefully./// </summary>/// <param name="a"></param>/// <param name="b"></param>/// <returns></returns>public static bool operator ==(ResponseStatusCode a, ResponseStatusCode b){return a?.ToString() == b?.ToString();}
/// <summary>/// Compares based on the .ToString() method/// </summary>/// <param name="o"></param>/// <returns></returns>public override bool Equals(object o){return this.ToString() == o?.ToString();}
/// <summary>/// Compares based on the .Name property/// </summary>/// <returns></returns>public override int GetHashCode(){return this.Name.GetHashCode();}}}
Enum.GetName(typeof(MyEnum), (int)MyEnum.FORMS)Enum.GetName(typeof(MyEnum), (int)MyEnum.WINDOWSAUTHENTICATION)Enum.GetName(typeof(MyEnum), (int)MyEnum.SINGLESIGNON)

产出是:

“表格”

"WINDOWS认证"

"SINGLESIGNON"

您可以声明枚举和字典,其中键将是枚举的值。将来,您可以参考字典来获取值。因此,可以将参数作为枚举的类型传递给函数,但要从字典中获取实值:

using System;using System.Collections.Generic;
namespace console_test{class Program{#region SaveFormatinternal enum SaveFormat{DOCX,PDF}
internal static Dictionary<SaveFormat,string> DictSaveFormat = new Dictionary<SaveFormat, string>\{\{ SaveFormat.DOCX,"This is value for DOCX enum item" },{ SaveFormat.PDF,"This is value for PDF enum item" }};
internal static void enum_value_test(SaveFormat save_format){Console.WriteLine(DictSaveFormat[save_format]);}#endregion
internal static void Main(string[] args){enum_value_test(SaveFormat.DOCX);//Output: This is value for DOCX enum itemConsole.Write("Press any key to continue . . . ");Console.ReadKey(true);}}}

我创建了一个用于在. NET中创建字符串值枚举的基类。它只是一个C#文件,您可以复制并粘贴到您的项目中,或者通过名为StringEnum.github回购的NuGet包安装

  • 如果类使用xml注释<completitionlist>进行注释,则Intellisense将建议枚举名称。(适用于C#和VB)

智能感知演示

  • 类似于常规枚举的用法:
///<completionlist cref="HexColor"/>class HexColor : StringEnum<HexColor>{public static readonly HexColor Blue = Create("#FF0000");public static readonly HexColor Green = Create("#00FF00");public static readonly HexColor Red = Create("#000FF");}
    // Static Parse MethodHexColor.Parse("#FF0000") // => HexColor.RedHexColor.Parse("#ff0000", caseSensitive: false) // => HexColor.RedHexColor.Parse("invalid") // => throws InvalidOperationException
// Static TryParse method.HexColor.TryParse("#FF0000") // => HexColor.RedHexColor.TryParse("#ff0000", caseSensitive: false) // => HexColor.RedHexColor.TryParse("invalid") // => null
// Parse and TryParse returns the preexistent instancesobject.ReferenceEquals(HexColor.Parse("#FF0000"), HexColor.Red) // => true
// Conversion from your `StringEnum` to `string`string myString1 = HexColor.Red.ToString(); // => "#FF0000"string myString2 = HexColor.Red; // => "#FF0000" (implicit cast)

安装:

  • 将以下StringEnum基类粘贴到您的项目中。(最新版本
  • 或者安装String E num NuGet包,它基于.Net Standard 1.0,因此它在.Net Core>=1.0、.Net Framework>=4.5、Mono>=4.6等上运行。
    /// <summary>/// Base class for creating string-valued enums in .NET.<br/>/// Provides static Parse() and TryParse() methods and implicit cast to string./// </summary>/// <example>/// <code>/// class Color : StringEnum &lt;Color&gt;/// {///     public static readonly Color Blue = Create("Blue");///     public static readonly Color Red = Create("Red");///     public static readonly Color Green = Create("Green");/// }/// </code>/// </example>/// <typeparam name="T">The string-valued enum type. (i.e. class Color : StringEnum&lt;Color&gt;)</typeparam>public abstract class StringEnum<T> : IEquatable<T> where T : StringEnum<T>, new(){protected string Value;private static Dictionary<string, T> valueDict = new Dictionary<string, T>();protected static T Create(string value){if (value == null)return null; // the null-valued instance is null.
var result = new T() { Value = value };valueDict.Add(value, result);return result;}
public static implicit operator string(StringEnum<T> enumValue) => enumValue.Value;public override string ToString() => Value;
public static bool operator !=(StringEnum<T> o1, StringEnum<T> o2) => o1?.Value != o2?.Value;public static bool operator ==(StringEnum<T> o1, StringEnum<T> o2) => o1?.Value == o2?.Value;
public override bool Equals(object other) => this.Value.Equals((other as T)?.Value ?? (other as string));bool IEquatable<T>.Equals(T other) => this.Value.Equals(other.Value);public override int GetHashCode() => Value.GetHashCode();
/// <summary>/// Parse the <paramref name="value"/> specified and returns a valid <typeparamref name="T"/> or else throws InvalidOperationException./// </summary>/// <param name="value">The string value representad by an instance of <typeparamref name="T"/>. Matches by string value, not by the member name.</param>/// <param name="caseSensitive">If true, the strings must match case and takes O(log n). False allows different case but is little bit slower (O(n))</param>public static T Parse(string value, bool caseSensitive = true){var result = TryParse(value, caseSensitive);if (result == null)throw new InvalidOperationException((value == null ? "null" : $"'{value}'") + $" is not a valid {typeof(T).Name}");
return result;}
/// <summary>/// Parse the <paramref name="value"/> specified and returns a valid <typeparamref name="T"/> or else returns null./// </summary>/// <param name="value">The string value representad by an instance of <typeparamref name="T"/>. Matches by string value, not by the member name.</param>/// <param name="caseSensitive">If true, the strings must match case. False allows different case but is slower: O(n)</param>public static T TryParse(string value, bool caseSensitive = true){if (value == null) return null;if (valueDict.Count == 0) System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle); // force static fields initializationif (caseSensitive){if (valueDict.TryGetValue(value, out T item))return item;elsereturn null;}else{// slower O(n) case insensitive searchreturn valueDict.FirstOrDefault(f => f.Key.Equals(value, StringComparison.OrdinalIgnoreCase)).Value;// Why Ordinal? => https://esmithy.net/2007/10/15/why-stringcomparisonordinal-is-usually-the-right-choice/}}}