C# 枚举的字符串表示
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/424366/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
String representation of an Enum
提问by user29964
I have the following enumeration:
我有以下枚举:
public enum AuthenticationMethod
{
FORMS = 1,
WINDOWSAUTHENTICATION = 2,
SINGLESIGNON = 3
}
The problem however is that I need the word "FORMS" when I ask for AuthenticationMethod.FORMS and not the id 1.
然而,问题是当我要求 AuthenticationMethod.FORMS 而不是 id 1 时,我需要“FORMS”这个词。
I have found the following solution for this problem (link):
我找到了针对此问题的以下解决方案(链接):
First I need to create a custom attribute called "StringValue":
首先,我需要创建一个名为“StringValue”的自定义属性:
public class StringValue : System.Attribute
{
private readonly string _value;
public StringValue(string value)
{
_value = value;
}
public string Value
{
get { return _value; }
}
}
Then I can add this attribute to my enumerator:
然后我可以将此属性添加到我的枚举器中:
public enum AuthenticationMethod
{
[StringValue("FORMS")]
FORMS = 1,
[StringValue("WINDOWS")]
WINDOWSAUTHENTICATION = 2,
[StringValue("SSO")]
SINGLESIGNON = 3
}
And of course I need something to retrieve that StringValue:
当然,我需要一些东西来检索该 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;
}
}
Good now I've got the tools to get a string value for an enumerator. I can then use it like this:
很好,现在我有了为枚举器获取字符串值的工具。然后我可以像这样使用它:
string valueOfAuthenticationMethod = StringEnum.GetStringValue(AuthenticationMethod.FORMS);
Okay now all of these work like a charm but I find it a whole lot of work. I was wondering if there is a better solution for this.
好的,现在所有这些都像魅力一样工作,但我发现它需要大量工作。我想知道是否有更好的解决方案。
I also tried something with a dictionary and static properties but that wasn't better either.
我还尝试了一些带有字典和静态属性的东西,但这也不是更好。
采纳答案by Jakub ?turc
Try type-safe-enumpattern.
尝试类型安全枚举模式。
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;
}
}
UpdateExplicit (or implicit) type conversion can be done by
更新显式(或隐式)类型转换可以通过
adding static field with mapping
private static readonly Dictionary<string, AuthenticationMethod> instance = new Dictionary<string,AuthenticationMethod>();
- n.b. In order that the initialisation of the the "enum member" fields doesn't throw a NullReferenceException when calling the instance constructor, be sure to put the Dictionary field before the "enum member" fields in your class. This is because static field initialisers are called in declaration order, and before the static constructor, creating the weird and necessary but confusing situation that the instance constructor can be called before all static fields have been initialised, and before the static constructor is called.
filling this mapping in instance constructor
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; else throw new InvalidCastException(); }
添加带有映射的静态字段
private static readonly Dictionary<string, AuthenticationMethod> instance = new Dictionary<string,AuthenticationMethod>();
- 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(); }
回答by Charles Bretana
Use method
使用方法
Enum.GetName(Type MyEnumType, object enumvariable)
as in (Assume Shipper
is a defined Enum)
如(假设Shipper
是一个定义的枚举)
Shipper x = Shipper.FederalExpress;
string s = Enum.GetName(typeof(Shipper), x);
There are a bunch of other static methods on the Enum class worth investigating too...
Enum 类上还有许多其他静态方法值得研究......
回答by Keith
Unfortunately reflection to get attributes on enums is quite slow:
不幸的是,在枚举上获取属性的反射非常慢:
See this question: Anyone know a quick way to get to custom attributes on an enum value?
看到这个问题:任何人都知道一种快速获取枚举值上的自定义属性的方法吗?
The .ToString()
is quite slow on enums too.
该.ToString()
是枚举很慢了。
You can write extension methods for enums though:
您可以为枚举编写扩展方法:
public static string GetName( this MyEnum input ) {
switch ( input ) {
case MyEnum.WINDOWSAUTHENTICATION:
return "Windows";
//and so on
}
}
This isn't great, but will be quick and not require the reflection for attributes or field name.
这不是很好,但会很快并且不需要属性或字段名称的反射。
C#6 Update
C#6 更新
If you can use C#6 then the new nameof
operator works for enums, so nameof(MyEnum.WINDOWSAUTHENTICATION)
will be converted to "WINDOWSAUTHENTICATION"
at compile time, making it the quickest way to get enum names.
如果您可以使用 C#6,那么 newnameof
运算符适用于枚举,因此nameof(MyEnum.WINDOWSAUTHENTICATION)
将"WINDOWSAUTHENTICATION"
在编译时转换为,使其成为获取枚举名称的最快方法。
Note that this will convert the explicit enum to an inlined constant, so it doesn't work for enums that you have in a variable. So:
请注意,这会将显式枚举转换为内联常量,因此它不适用于变量中的枚举。所以:
nameof(AuthenticationMethod.FORMS) == "FORMS"
But...
但...
var myMethod = AuthenticationMethod.FORMS;
nameof(myMethod) == "myMethod"
回答by BenAlabaster
You can reference the name rather than the value by using ToString()
您可以使用 ToString() 引用名称而不是值
Console.WriteLine("Auth method: {0}", AuthenticationMethod.Forms.ToString());
The documentation is here:
文档在这里:
http://msdn.microsoft.com/en-us/library/16c1xs4z.aspx
http://msdn.microsoft.com/en-us/library/16c1xs4z.aspx
...and if you name your enums in Pascal Case (as I do - such as ThisIsMyEnumValue = 1 etc.) then you could use a very simple regex to print the friendly form:
...如果你在 Pascal Case 中命名你的枚举(就像我所做的 - 例如 ThisIsMyEnumValue = 1 等)那么你可以使用一个非常简单的正则表达式来打印友好的形式:
static string ToFriendlyCase(this string EnumString)
{
return Regex.Replace(EnumString, "(?!^)([A-Z])", " ");
}
which can easily be called from any string:
可以很容易地从任何字符串调用:
Console.WriteLine("ConvertMyCrazyPascalCaseSentenceToFriendlyCase".ToFriendlyCase());
Outputs:
输出:
Convert My Crazy Pascal Case Sentence To Friendly Case
将我的疯狂帕斯卡案例句子转换为友好案例
That saves running all the way around the houses creating custom attributes and attaching them to your enums or using lookup tables to marry an enum value with a friendly string and best of all it's self managing and can be used on any Pascal Case string which is infinitely more reusable. Of course, it doesn't allow you to have a differentfriendly name than your enum which your solution does provide.
这节省了在房子周围运行创建自定义属性并将它们附加到您的枚举或使用查找表将枚举值与友好字符串结合的情况,最重要的是它是自我管理的,并且可以用于任何无限的 Pascal Case 字符串更可重复使用。当然,它不允许您使用与解决方案提供的枚举不同的友好名称。
I do like your original solution though for more complex scenarios though. You could take your solution one step further and make your GetStringValue an extension method of your enum and then you wouldn't need to reference it like StringEnum.GetStringValue...
不过,对于更复杂的场景,我确实喜欢您的原始解决方案。您可以将您的解决方案更进一步,让您的 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;
}
You could then access it easily straight from your enum instance:
然后您可以直接从您的枚举实例轻松访问它:
Console.WriteLine(AuthenticationMethod.SSO.GetStringValue());
回答by Ray Booysen
I use the Description attribute from the System.ComponentModel namespace. Simply decorate the enum and then use this code to retrieve it:
我使用 System.ComponentModel 命名空间中的 Description 属性。只需装饰枚举,然后使用此代码来检索它:
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 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();
}
As an example:
举个例子:
public enum Cycle : int
{
[Description("Daily Cycle")]
Daily = 1,
Weekly,
Monthly
}
This code nicely caters for enums where you don't need a "Friendly name" and will return just the .ToString() of the enum.
这段代码很好地满足了不需要“友好名称”的枚举,并且只返回枚举的 .ToString() 。
回答by Tony Basallo
I agree with Keith, but I can't vote up (yet).
我同意基思,但我不能投票(还)。
I use a static method and swith statement to return exactly what I want. In the database I store tinyint and my code only uses the actual enum, so the strings are for UI requirements. After numerous testing this resulted in the best performance and most control over the output.
我使用静态方法和 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;
}
}
However, by some accounts, this leads to a possible maintenance nightmare and some code smell. I try to keep an eye for enums that are long and a lot of enums, or those that change frequently. Otherwise, this has been a great solution for me.
然而,根据某些说法,这会导致可能的维护噩梦和一些代码异味。我试图留意那些很长的枚举和大量的枚举,或者那些频繁变化的枚举。否则,这对我来说是一个很好的解决方案。
回答by Pablo Retyk
Option 1:
选项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";}
}
and then
进而
object auth = new SsoAuth(); //or whatever
//...
//...
// blablabla
DoSomethingWithTheAuth(auth.ToString());
Option 2:
选项 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");
}
}
回答by Steve Mitcham
I use a combination of several of the suggestions above, combined with some caching. Now, I got the idea from some code that I found somewhere on the net, but I can neither remember where I got it or find it. So if anyone ever finds something that looks similar please comment with the attribution.
我结合了上述几个建议,并结合了一些缓存。现在,我从网上某处找到的一些代码中得到了这个想法,但我既不记得我从哪里得到它,也无法找到它。因此,如果有人发现看起来相似的东西,请评论出处。
Anyway, the usage involves the type converters, so if you are binding to the UI it 'just works'. You can extended with Jakub's pattern for quick code lookup by initializing from the type converter into the static methods.
无论如何,用法涉及类型转换器,因此如果您绑定到 UI,它就可以“正常工作”。您可以使用 Jakub 的模式进行扩展,通过从类型转换器初始化为静态方法来快速查找代码。
The base usage would look like this
基本用法如下所示
[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
}
The code for the custom enum type converter follows:
自定义枚举类型转换器的代码如下:
public class CustomEnumTypeConverter<T> : EnumConverter
where 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);
}
}
}
}
回答by Robert Rossney
When I'm confronted with this problem, there are a couple of questions that I try to find the answers to first:
当我遇到这个问题时,有几个问题我会首先尝试找到答案:
- Are the names of my enum values sufficiently friendly for the purpose, or do I need to provide friendlier ones?
- Do I need to round-trip? That is, will I need to take text values and parse them into enum values?
- Is this something I need to do for many enums in my project, or just one?
- What kind of UI elements will I be presenting this information in - in particular, will I be binding to the UI, or using property sheets?
- Does this need to be localizable?
- 我的枚举值的名称是否足够友好,还是我需要提供更友好的名称?
- 我需要往返吗?也就是说,我是否需要获取文本值并将它们解析为枚举值?
- 这是我需要为我的项目中的许多枚举做的事情,还是只需要一个?
- 我将在哪些类型的 UI 元素中呈现此信息 - 特别是,我将绑定到 UI,还是使用属性表?
- 这需要本地化吗?
The simplest way to do this is with Enum.GetValue
(and support round-tripping using Enum.Parse
). It's also often worth building a TypeConverter
, as Steve Mitcham suggests, to support UI binding. (It's not necessary to build a TypeConverter
when you're using property sheets, which is one of the nice things about property sheets. Though lord knows they have their own issues.)
最简单的方法是使用Enum.GetValue
(并支持往返使用Enum.Parse
)。TypeConverter
正如 Steve Mitcham 所建议的那样,通常还值得构建一个. 来支持 UI 绑定。(TypeConverter
使用属性表时没有必要构建一个,这是属性表的优点之一。虽然上帝知道他们有自己的问题。)
In general, if the answers to the above questions suggest that's not going to work, my next step is to create and populate a static Dictionary<MyEnum, string>
, or possibly a Dictionary<Type, Dictionary<int, string>>
. I tend to skip the intermediate decorate-the-code-with-attributes step because what's usually coming down the pike next is the need to change the friendly values after deployment (often, but not always, because of localization).
一般来说,如果上述问题的答案表明这行不通,我的下一步是创建并填充一个 static Dictionary<MyEnum, string>
,或者可能是一个Dictionary<Type, Dictionary<int, string>>
. 我倾向于跳过使用属性装饰代码的中间步骤,因为接下来通常需要在部署后更改友好值(通常,但并非总是如此,因为本地化)。
回答by Harvo
If you think about the problem we're trying to solve, it's not an enum we need at all. We need an object that allows a certain number of values to be associated with eachother; in other words, to define a class.
如果您考虑我们要解决的问题,它根本不是我们需要的枚举。我们需要一个允许一定数量的值相互关联的对象;换句话说,定义一个类。
Jakub ?turc's type-safe enum pattern is the best option I see here.
Jakub ?turc 的类型安全枚举模式是我在这里看到的最佳选择。
Look at it:
看它:
- It has a private constructor so only the class itself can define the allowed values.
- It is a sealed class so values can't be modifed through inheritence.
- It is type-safe, allowing your methods to require only that type.
- There is no reflection performance hit incurred by accessing the values.
- And lastly, it can be modified to associate more than two fields together, for example a Name, Description, and a numeric Value.
- 它有一个私有构造函数,所以只有类本身可以定义允许的值。
- 它是一个密封类,因此不能通过继承修改值。
- 它是类型安全的,允许您的方法只需要该类型。
- 访问这些值不会影响反射性能。
- 最后,可以修改它以将两个以上的字段关联在一起,例如名称、描述和数值。