枚举的字符串表示形式


911

我有以下列举:

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

但是问题是,当我要求AuthenticationMethod.FORMS而不是ID 1时,我需要单词“ FORMS”。

我已经找到以下针对此问题的解决方案(link):

首先,我需要创建一个名为“ 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);

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

我也尝试了一些具有字典和静态属性的方法,但这也不是更好。


8
真好!我可以使用它来将枚举值转换为本地化的字符串。
岛之风Skaar

5
尽管您可能会发现这是一个漫长的过程,但实际上这是进行其他事情的一种非常灵活的方法。正如我的一位同事指出的那样,在许多情况下都可以使用它来代替将数据库代码映射为枚举值的枚举帮助器……
BenAlabaster

27
MSDN建议使用带有“属性”后缀的后缀属性类。所以“类StringValueAttribute”;)
serhio 2011年

14
我同意@BenAlabaster,这实际上非常灵活。另外,只需在静态方法中添加this,就可以将其作为扩展Enum方法。然后,您可以做 AuthenticationMethod.Forms.GetStringValue();
贾斯汀·皮洪尼

5
这种方法使用反射来读取属性值,并且根据我的经验,如果必须多次调用GetStringValue(),它将非常慢。类型安全枚举模式更快。
Rn222

Answers:


868

尝试使用类型安全的枚举模式。

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

17
它看起来像一个枚举,但不是枚举。我可以想象,如果人们开始尝试比较AuthenticationMethods,就会引起一些有趣的问题。您可能还需要重载各种相等运算符。
蚂蚁

36
@Ant:我不必。由于每个AuthenticationMethod只有一个实例,因此从Object继承的引用相等性很好。
JakubŠturc'10

10
@tyriker:编译器可以。构造函数是私有的,因此您不能创建新实例。而且静态成员不能通过实例访问。
JakubŠturc2010年

21
@Jakub非常有趣。我必须玩弄它才能弄清楚如何使用它,并认识到它的好处。这是一个公共的非静态类,但是无法实例化,您只能访问其静态成员。基本上,它的行为像枚举。但是最好的部分是……静态成员是类的类型,而不是通用字符串或int。这是一个[等待] ...输入安全枚举!感谢您帮助我理解。
tyriker 2010年

6
@kiran我在下面发布了JakubŠturc的答案的略微修改版本,使其可以与Switch-Case语句一起使用,因此,此方法没有任何缺点:)
deadlydog 2014年

228

使用方法

Enum.GetName(Type MyEnumType,  object enumvariable)  

如(假设Shipper是已定义的Enum)

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

Enum类上还有许多其他静态方法也值得研究...


5
究竟。我确实为字符串描述创建了一个自定义属性,但这是因为我想要一个易于使用的版本(包含空格和其他特殊字符),可以轻松地绑定到ComboBox等。
lc。

5
Enum.GetName反映枚举中的字段名称-与.ToString()相同。如果性能是一个问题,那就可能是一个问题。除非您要转换枚举的负载,否则我不会担心。
基思

8
如果需要一个具有额外功能的枚举,可以考虑的另一种选择是使用结构“自己滚动” ...添加静态只读命名属性来表示初始化为生成该结构各个实例的构造函数的枚举值...
查尔斯·布雷塔纳

1
那么您可以添加任何您想要的其他结构成员,以实现您希望此“枚举”具有的任何功能...
Charles Bretana 09年

2
这里的问题是GetName不可本地化。这并不总是一个问题,但是需要注意。
Joel Coehoorn

79

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

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

文档在这里:

http://msdn.microsoft.com/zh-CN/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案例句子转换为友好案例

这样可以省去创建自定义属性并将其附加到枚举或使用查找表将枚举值与友好字符串结合在一起的麻烦,并且最好是自管理,并且可以在任何无穷大的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());

2
如果“好记的名称”需要空格,这无济于事。例如“表单身份验证”
Ray Booysen,2009年

4
因此,请确保枚举以诸如FormsAuthentication之类的大写字母命名,并在所有不在开头的大写字母之前插入一个空格。在字符串中插入空格不是火箭科学……
BenAlabaster

4
如果Pascal Case名称包含应大写的缩写(例如XML或GPS),则自动间距会成为问题。
理查德·埃夫

2
@RichardEv,没有完美的正则表达式,但是这里的缩写应该可以更好地工作。"(?!^)([^A-Z])([A-Z])", "$1 $2"。如此HereIsATEST成为Here Is ATEST
13年

不要小声地做这些小“骇客”。我明白了OP在说什么,我正在尝试找到一个类似的解决方案,即使用Enums的优雅之处,但能够轻松访问相关消息。我能想到的唯一解决方案是在枚举名称和字符串值之间应用某种映射,但这并不能解决维护字符串数据的问题(但是,对于需要多个区域等的情况而言,它很实用。 )
塔希尔·哈立德

72

不幸的是,在枚举中获取属性的反思非常缓慢:

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

.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"

24
您可以一次获取属性值并将其放入Dictionary <MyEnum,string>中以保留声明性方面。
乔恩·斯基特

1
是的,当我们发现反射是瓶颈时,这就是我们最终在带有大量枚举的应用程序中执行的操作。
基思

感谢Jon和Keith,我最终使用了您的Dictionary建议。效果很好(而且速度很快!)。
Helge Klein

@JonSkeet我知道这很旧。但是,如何实现这一目标呢?
user919426

2
@ user919426:实现想要吗?将它们放入字典中?只需创建一个字典,理想情况下使用集合初始值设定项即可...尚不清楚您要的是什么。
乔恩·斯基特

59

我使用扩展方法:

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"


1
我还必须添加using System.ComponentModel;,仅当您希望String值与Enum的名称相同时,此方法才有效。OP希望获得不同的价值。
Elcool

2
你不是在打电话的意思 AuthenticationMethod.FORMS.ToDescription()吗?
nicodemus13 2014年

41

只需使用ToString()方法

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

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

any.Tomato.ToString();

哇。那很简单。我知道OP想要添加自定义字符串描述,但这就是我所需要的。回想起来,我应该知道尝试一下,但是我沿着Enum.GetName路线走了。
拉菲2012年

7
为什么其他所有人都使这一问题变得复杂呢?
布伦特2014年

18
@Brent因为大多数情况下,您所获得的.ToString()价值与所需的用户友好型价值有所不同。
Novitchi S 2014年

2
@Brent-因为这与所问的问题不同。所要问的问题是如何从分配了枚举值的变量中获取此字符串。在运行时这是动态的。这是在检查类型的定义并在运行时进行设置。
Hogan

1
@Hogan-ToString()也适用于变量: any fruit = any.Tomato; string tomato = fruit.ToString();
LiborV

29

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

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

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

MyStatus.Active.ToString("f");

要么

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

该值将为“有效”或“已归档”。

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


28

我使用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();

        }

举个例子:

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

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


27

我真的很喜欢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;      
}

较短的解决方案是删除枚举{},而对要构造的枚举进行静态计数。这也带来了好处,您不必向枚举列表添加新实例。例如,public static int nextAvailable { get; private set; }然后在构造函数中this.Value = nextAvailable++;
kjhf 2014年

有趣的主意@kjhf。不过,我担心的是,如果有人重新排序代码,则分配给枚举值的值也可能会更改。例如,当将枚举值保存到文件/数据库,更改“ new AuthenticationMethod(...)”行的顺序(例如,删除一行)后,这可能导致检索到错误的枚举值。再次运行应用程序,并从文件/数据库中检索枚举值;枚举值可能与最初保存的AuthenticationMethod不匹配。
deadlydog 2014年

很好-尽管我希望在这些特殊情况下,人们不会依赖枚举的整数值(或重新排序枚举代码。),并且此值纯粹用作开关,并且可能替代.Equals()和。 GetHashCode()。如果您有疑虑,可以随时在“ DO RE REORDER”中留言:p
kjhf 2014年

您能不能让=操作员超负荷工作以允许开关工作?我在VB中进行了此操作,现在可以在select case语句中使用它。
user1318499 2014年

@ user1318499不,C#在switch语句方面的规则比VB更严格。您不能对Case语句使用类实例;您只能使用常量原语。
deadlydog 2014年

13

我结合了上面的一些建议和一些缓存。现在,我从网上找到的一些代码中得到了这个主意,但是我不记得在哪里找到它或在哪里找到它。因此,如果有人找到相似的内容,请注明出处。

无论如何,用法都涉及类型转换器,因此,如果您绑定到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> : 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);
    }
}

}


12

在您的问题中,您从未说过您实际上在任何地方都需要枚举的数值。

如果不这样做,只需要一个字符串类型的枚举(它不是整数类型,就不能成为枚举的基础),这是一种方法:

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

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

if (bla == AuthenticationMethod.FORMS)

这将比使用数字值(比较字符串而不是数字)要慢一些,但从正面看,它没有使用反射(慢速)来访问字符串。


如果使用“ const”而不是“ static readonly”,则可以在switch语句中将这些值用作大小写标签。
Ed N.

11

我如何解决此问题作为扩展方法:

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 and Refill”。


为了完整起见,您应该包括一个DescriptionAttribute类的副本。
伯尼·怀特

3
伯尼(Bernie),DescriptionAttribute位于System.ComponentModel中
agentnega 2012年

11

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

如果您正在阅读本文,请确保您还查看其他答案。
(提示:它们高于此值)


像你们大多数人一样,我真的很喜欢JakubŠturc选择的答案,但是我也非常讨厌复制粘贴代码,并尽我所能去做。

因此,我决定想要一个EnumBase类,从该类继承/内置了大多数功能,让我专注于内容而不是行为。

这种方法的主要问题基于以下事实:尽管Enum值是类型安全的实例,但交互作用是与Enum Class类型的Static实现。因此,在一点泛型魔术的帮助下,我认为我终于有了正确的搭配。希望有人能像我一样有用。

我将从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 class
public class EnumBase<E, T> where E: EnumBase<E, T>
{
    #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<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
}

您可以从基本静态构造函数调用派生的静态构造函数。我仍然在寻找到它,但到目前为止,我已经发现了与它没有任何问题:stackoverflow.com/questions/55290034/...
科里-G

10

我同意基思(Keith)的观点,但我无法投票(尚未)。

我使用静态方法和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;
     }
}

但是,由于某些原因,这可能导致维护方面的噩梦和某些代码异味。我试图留意那些枚举长,枚举很多或经常更改的枚举。否则,这对我来说是一个很好的解决方案。


10

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

    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;

2
最好将变量设为const而不是使用static readonly
AndyGeek

1
const不适用于可公开访问的类,因为它们是在编译时烘焙的,如果不使用const重新编译整个代码,则无法替换第三方DLL。const与静态只读的性能偏移可以忽略不计。
克里斯蒂安·威廉姆斯

7

当我遇到这个问题时,有几个问题我想首先找到答案:

  • 我的枚举值的名称是否足够友好用于此目的,或者我需要提供更友好的名称?
  • 我需要往返吗?也就是说,我是否需要获取文本值并将其解析为枚举值?
  • 这是我需要为项目中的多个枚举(还是仅一个枚举)执行的操作?
  • 我将使用哪种UI元素来呈现此信息-特别是将其绑定到UI还是使用属性表?
  • 这需要本地化吗?

最简单的方法是使用Enum.GetValue(并支持使用往返Enum.Parse)。TypeConverter正如Steve Mitcham所建议的那样,通常也值得构建一个以支持UI绑定。(没有必要建立一个TypeConverter在使用属性表时,,这是关于属性表的优点之一。尽管上帝知道它们有其自身的问题。)

通常,如果上述问题的答案表明将无法正常工作,那么我的下一步是创建并填充一个static Dictionary<MyEnum, string>或可能的a Dictionary<Type, Dictionary<int, string>>。我倾向于跳过中间的“用属性修饰代码”步骤,因为接下来通常是在部署之后需要更改友好值(通常但由于本地化而并非总是如此)。


7

我想将其发布为对下面引用的帖子的评论,但不能这样做,因为我没有足够的代表-所以请不要投票。代码中包含错误,我想向尝试使用此解决方案的个人指出:

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

太棒了!


5

我的变体

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)。


4

选项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");
    }
}

4

如果您考虑我们要解决的问题,那么这根本不是我们需要的枚举。我们需要一个允许一定数量的值相互关联的对象。换句话说,定义一个类。

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

看它:

  • 它具有私有构造函数,因此只有类本身才能定义允许的值。
  • 这是一个密封的类,因此无法通过继承修改值。
  • 它是类型安全的,允许您的方法仅要求该类型。
  • 访问这些值不会导致反射性能下降。
  • 最后,可以对其进行修改以将两个以上的字段关联在一起,例如,名称,说明和数字值。

4

对我来说,务实的方法是在班级里上课,示例:

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

4

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

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

Intellisense演示

  • 用法与常规枚举类似:
///<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 Method
    HexColor.Parse("#FF0000") // => HexColor.Red
    HexColor.Parse("#ff0000", caseSensitive: false) // => HexColor.Red
    HexColor.Parse("invalid") // => throws InvalidOperationException

    // Static TryParse method.
    HexColor.TryParse("#FF0000") // => HexColor.Red
    HexColor.TryParse("#ff0000", caseSensitive: false) // => HexColor.Red
    HexColor.TryParse("invalid") // => null

    // Parse and TryParse returns the preexistent instances
    object.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基类粘贴到您的项目中。(最新版本
  • 或安装StringEnum 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 initialization
            if (caseSensitive)
            {
                if (valueDict.TryGetValue(value, out T item))
                    return item;
                else
                    return null;
            }
            else
            {
                // slower O(n) case insensitive search
                return 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/
            }
        }
    }

3

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

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

您可以将相关枚举归为一组。由于此方法使用枚举类型,因此在执行以下操作时,可以使用Intellisense显示枚举列表。GetString()调用。

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


3

这里有很多很好的答案,但就我而言,这并不能解决我从“字符串枚举”中想要的东西:

  1. 在switch语句中可用,例如switch(myEnum)
  2. 可以在函数参数中使用,例如foo(myEnum type)
  3. 可以引用例如myEnum.FirstElement
  4. 我可以使用字符串,例如foo(“ FirstElement”)== foo(myEnum.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的地方。但是不利的是,任何字符串都自动是有效的枚举,可以对其进行修改,但是随后它将需要某种init函数...或者可能使它们显式转换为内部?

现在,如果一个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;
        else
            throw 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

}

但当然是“ Types bob = 4;” 除非您先对其进行了初始化,否则将毫无意义……

但是理论上TypeA == TypeB会更快...


3

如果我对您的理解正确,则可以简单地使用.ToString()从值中检索枚举的名称(假定它已被强制转换为Enum);如果您有裸露的int(可以说是从数据库之类的东西),则可以先将其转换为枚举。以下两种方法都会为您提供枚举名称。

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

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




2

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

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

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

资源名称资源值
Color_Red我的字符串颜色为红色
颜色_蓝色Blu​​eeey
颜色_绿巨人颜色

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


1

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

作为消费阶层,您可以

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),但更加优雅。该字典也是可枚举的,您可以从整数到字符串来回切换。另外,除了此类之外,您的代码库中也不必包含任何字符串。

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

对于较大的字符串枚举集,列出的示例可能会令人厌烦。如果需要状态代码列表或其他基于字符串的枚举列表,则使用属性系统很烦人,而带有自身实例的静态类则很烦人。对于我自己的解决方案,我使用T4模板来简化具有字符串支持的枚举的过程。结果类似于HttpMethod类的工作原理。

您可以像这样使用它:

    string statusCode = ResponseStatusCode.SUCCESS; // Automatically converts to string when needed
    ResponseStatusCode 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 values
    bool 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();
        }
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.