我有以下列举:

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

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

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

首先,我需要创建一个自定义属性“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);

好的,现在所有这些工作就像一个魅力,但我发现它有很多工作。我想知道有没有更好的解决办法。

我还尝试了一些字典和静态属性,但这也不是更好。


当前回答

我很清楚这个问题已经被回答了,而且OP已经对接受的答案感到满意。但我发现大多数答案,包括公认的答案,都有点复杂。

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

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

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

其他回答

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

如果类被注释为xml comment <completitionlist>,智能感知将提示枚举名称。(适用于c#和VB)

类似于普通enum的用法:

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

Instalation:

将下面的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/
            }
        }
    }

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

}

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

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

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

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 Value
Color_Red              My String Color in Red
Color_Blue             Blueeey
Color_Green            Hulk Color

这样做的好处是,如果您需要本地化应用程序,它将非常有用,因为您所需要做的只是用新语言创建另一个资源文件!和Voe-la !

当我遇到这样的问题时,我首先会试图找到以下几个问题的答案:

枚举值的名称是否足够友好,或者我需要提供更友好的名称? 我需要往返吗?也就是说,我是否需要将文本值解析为枚举值? 这是我需要为我的项目中的许多枚举做的事情,还是只有一个? 我将在什么样的UI元素中呈现这些信息-特别是,我将绑定到UI,还是使用属性表? 这需要本地化吗?

最简单的方法是使用Enum。GetValue(并使用枚举. parse支持往返)。正如Steve Mitcham所建议的那样,构建一个TypeConverter来支持UI绑定通常也是值得的。(在使用属性表时,没有必要构建TypeConverter,这是属性表的优点之一。尽管天晓得他们也有自己的问题。)

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

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

[TypeConverter typeof (CustomEnumTypeConverter (typeof (MyEnum))) public enum MyEnum { //自定义类型转换器将使用description属性 [描述(“自定义描述”)] ValueWithCustomDescription, //这将完全暴露。 确切的 }

应该是

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