我有以下枚举:
public enum AuthenticationMethod { FORMS = 1, WINDOWSAUTHENTICATION = 2, SINGLESIGNON = 3 }
然而问题是,当我要求AuthenticationMethod.FORMS而不是id 1时,我需要"FORMS"这个词.
我找到了以下解决此问题的方法(链接):
首先,我需要创建一个名为"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);
好的,现在所有这些都像魅力一样,但我发现它做了很多工作.我想知道是否有更好的解决方案.
我也尝试过使用字典和静态属性的东西,但这也不是更好.
尝试type-safe-enum模式.
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 Dictionaryinstance = new Dictionary ();
nb为了在调用实例构造函数时初始化"枚举成员"字段不会抛出NullReferenceException,请确保将Dictionary字段放在类中的"枚举成员"字段之前.这是因为静态字段初始化程序是按声明顺序调用的,并且在静态构造函数之前,创建了奇怪且必要但令人困惑的情况,即在初始化所有静态字段之前,以及在调用静态构造函数之前,可以调用实例构造函数.
在实例构造函数中填充此映射
instance[name] = this;
并添加用户定义的类型转换运算符
public static explicit operator AuthenticationMethod(string str) { AuthenticationMethod result; if (instance.TryGetValue(str, out result)) return result; else throw new InvalidCastException(); }
使用方法
Enum.GetName(Type MyEnumType, object enumvariable)
如(假设Shipper
是一个定义的枚举)
Shipper x = Shipper.FederalExpress; string s = Enum.GetName(typeof(Shipper), x);
Enum类还有很多其他静态方法值得研究......
您可以使用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成为枚举的扩展方法,然后您不需要像StringEnum.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());
不幸的是,在枚举上获取属性的反射非常慢:
看到这个问题:任何人都知道快速获取枚举值的自定义属性的方法吗?
该.ToString()
是枚举很慢了.
您可以为枚举编写扩展方法:
public static string GetName( this MyEnum input ) { switch ( input ) { case MyEnum.WINDOWSAUTHENTICATION: return "Windows"; //and so on } }
这不是很好,但会很快,不需要反映属性或字段名称.
C#6更新
如果您可以使用C#6,那么new nameof
运算符适用于枚举,因此nameof(MyEnum.WINDOWSAUTHENTICATION)
将"WINDOWSAUTHENTICATION"
在编译时转换为,这使得它成为获取枚举名称的最快方法.
请注意,这会将显式枚举转换为内联常量,因此它不适用于变量中的枚举.所以:
nameof(AuthenticationMethod.FORMS) == "FORMS"
但...
var myMethod = AuthenticationMethod.FORMS; nameof(myMethod) == "myMethod"
我使用扩展方法:
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"
.
只需使用该ToString()
方法
public enum any{Tomato=0,Melon,Watermelon}
要引用该字符串Tomato
,只需使用
any.Tomato.ToString();
使用.Net 4.0及更高版本的解决方案非常简单.不需要其他代码.
public enum MyStatus { Active = 1, Archived = 2 }
要获取有关使用的字符串:
MyStatus.Active.ToString("f");
要么
MyStatus.Archived.ToString("f");`
该值将为"活动"或"已存档".
要在调用时Enum.ToString
查看不同的字符串格式(上面的"f"),请参阅此枚举格式字符串页面
我使用System.ComponentModel命名空间中的Description属性.只需修饰枚举,然后使用此代码检索它:
public static string GetDescription(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 enum MemberInfo[] 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 value return ((DescriptionAttribute)attrs[0]).Description; } } //If we have no description attribute, just return the ToString of the enum return enumerationValue.ToString(); }
举个例子:
public enum Cycle : int { [Description("Daily Cycle")] Daily = 1, Weekly, Monthly }
这段代码非常适合您不需要"友好名称"的枚举,并且只返回枚举的.ToString().
我真的很喜欢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 something break; case authenticationMethodVariable.Values.Windows: // Do something break; case authenticationMethodVariable.Values.SSN: // Do something break; }
我结合上面的一些建议,结合一些缓存.现在,我从网上某处找到的一些代码中得到了这个想法,但我既不记得我在哪里找到它或者找到它.因此,如果有人发现类似的内容,请对归因进行评论.
无论如何,用法涉及类型转换器,所以如果你绑定到UI它'只是工作'.通过从类型转换器初始化为静态方法,您可以扩展Jakub的模式以进行快速代码查找.
基本用法看起来像这样
[TypeConverter(typeof(CustomEnumTypeConverter))] 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: EnumConverter where T : struct { private static readonly Dictionary s_toString = new Dictionary (); private static readonly Dictionary s_toValue = new Dictionary (); 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); } }
}
在你的问题中,你从未说过你实际上需要枚举的数值.
如果你不这样做,只需要一个类型字符串的枚举(这不是一个整数类型,所以不能作为枚举的基础)这里是一种方式:
static class AuthenticationMethod { public static readonly string FORMS = "Forms", WINDOWSAUTHENTICATION = "WindowsAuthentication"; }
您可以使用与枚举相同的语法来引用它
if (bla == AuthenticationMethod.FORMS)
它会比使用数值(比较字符串而不是数字)慢一点,但在正面,它不使用反射(慢)来访问字符串.
作为大多数人,我真的很喜欢JakubŠturc选择的答案,但我也非常讨厌复制粘贴代码,并尽可能少地尝试.
所以我决定我想要一个EnumBase类,其中大部分功能都是继承/内置的,让我专注于内容而不是行为.
这种方法的主要问题是基于以下事实:虽然Enum值是类型安全的实例,但是交互是与Enum类类型的Static实现.所以在一点魔术的帮助下,我想我终于得到了正确的组合.希望有人发现这和我一样有用.
我将从Jakub的例子开始,但是使用继承和泛型:
public sealed class AuthenticationMethod : EnumBase{ 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 All { get { return EnumBase .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 class public class EnumBasewhere E: EnumBase { #region Instance code public 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 tools static private readonly Dictionary > mapping; static EnumBase() { mapping = new Dictionary >(); } protected static E Parse(string name) { EnumBase 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 All { get { return mapping.Values.AsEnumerable().Cast (); } } #endregion }
我如何解决这个扩展方法:
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()
这给了我一串"新卡"或"重新加载"而不是实际的枚举值NewCard和Refill.
我同意基思,但我还不能投票.
我使用静态方法和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; } }
但是,根据某些说法,这会导致可能的维护噩梦和一些代码味道.我试着留意那些很长很多枚举的词汇,或者经常变化的词汇.否则,这对我来说是一个很好的解决方案.
如果你来这里寻求实现一个简单的"枚举"但其值是字符串而不是整数,这是最简单的解决方案:
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;
当我遇到这个问题时,有几个问题我试图先找到答案:
我的枚举值的名称是否足够友好,或者我是否需要提供更友好的名称?
我需要往返吗?也就是说,我需要获取文本值并将它们解析为枚举值吗?
这是我需要为我的项目中的许多枚举做的事情,还是仅仅一个?
我将在哪些UI元素中呈现此信息 - 特别是,我将绑定到UI,还是使用属性表?
这需要可本地化吗?
最简单的方法是使用Enum.GetValue
(并支持使用round-tripping Enum.Parse
).TypeConverter
正如Steve Mitcham建议的那样,通常还需要建立一个支持UI绑定的方法.(TypeConverter
当你使用属性表时,没有必要建立一个属性表,这是关于属性表的好处之一.虽然主知道他们有自己的问题.)
一般来说,如果上述问题的答案表明它不起作用,我的下一步是创建并填充一个静态的Dictionary
,或者可能是一个Dictionary
.我倾向于跳过中间装饰 - 代码与属性步骤,因为接下来通常会出现在长矛上的是需要在部署后更改友好值(通常,但并非总是如此,因为本地化).
我想发布这个作为对下面引用的帖子的评论,但不能,因为我没有足够的代表 - 所以请不要投票.代码包含错误,我想向尝试使用此解决方案的个人指出:
[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))] public enum MyEnum { // The custom type converter will use the description attribute [Description("A custom description")] ValueWithCustomDescription, // This will be exposed exactly. Exact }
高明!
我的变体
public struct Colors { private String current; private static string red = "#ff0000"; private static string green = "#00ff00"; private static string blue = "#0000ff"; private static IListpossibleColors; 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 () {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)。