如何用字符串值定义一个枚举?


97

我试图定义一个,Enum并添加在CSV或类似文件中使用的有效通用分隔符。然后,我将其绑定到ComboBox作为数据源,因此,无论何时在Enum定义中添加或删除,都无需在组合框中进行任何更改。

问题是如何用字符串表示形式定义枚举,例如:

public enum SeparatorChars{Comma = ",", Tab = "\t", Space = " "}


Answers:


112

您不能-枚举值必须是整数值。您可以使用属性将字符串值与每个枚举值相关联,或者在这种情况下,如果每个分隔符都是单个字符,则可以只使用该char值:

enum Separator
{
    Comma = ',',
    Tab = '\t',
    Space = ' '
}

(编辑:为了澄清起见,您不能使char枚举的基础类型,但可以使用char常量来分配与每个枚举值相对应的整数值。上述枚举的基础类型是int。)

然后,如果需要一种扩展方法:

public string ToSeparatorString(this Separator separator)
{
    // TODO: validation
    return ((char) separator).ToString();
}

Char在枚举中无效。Msdn:“每个枚举类型都有一个基础类型,可以是除char之外的任何整数类型。”
dowhilefor 2011年

8
@dowhilefor:根据我的回答,您可以使用char文字作为。我对其进行了测试:)
Jon Skeet

由于此要求是针对文件的,因此用户可能需要CRLF分隔符。它也适用于这种情况吗?
Maheep 2011年

谢谢乔恩,\ t不算作字符吗?!
赛义德·亚兹达尼

1
@ShaunLuttin:枚举只是“命名数字”-因此,字符串枚举实际上根本不适合该模型。
乔恩·斯基特

82

据我所知,将不允许您将字符串值分配给枚举。您可以做的是创建一个带有字符串常量的类。

public static class SeparatorChars
{
    public static String Comma { get { return ",";} } 
    public static String Tab { get { return "\t,";} } 
    public static String Space { get { return " ";} } 
}

9
与其他方法相反的缺点是,如果不做额外的/特殊的事情,您将无法枚举这些方法。
caesay

这不利于在编译期间强制执行某些值,因为separator现在是字符串(可以是任何东西),而不是Separator有效值受限的类型。
ChickenFeet

70

您可以实现,但需要一些工作。

  1. 定义一个属性类,其中将包含枚举的字符串值。
  2. 定义一个扩展方法,该方法将从属性返回值。例如,.GetStringValue(此Enum值)将返回属性值。
  3. 然后,您可以像这样定义枚举。
公共枚举测试:int {
    [StringValue(“ a”)]
    Foo = 1
    [StringValue(“ b”)]
    某物= 2        
} 
  1. 从Attrinbute Test.Foo.GetStringValue();中获取值;

请参阅:C#中具有字符串值的枚举


4
我知道这个年代很久,但是它显然是唯一的,它允许您在数据库的代码和字符串值中使用枚举。Amazing
A_kat

1
另一个最新评论,但这确实是一个绝妙的解决方案
艾伦

35

对于字符串值(或任何其他类型)的简单枚举:

public static class MyEnumClass
{
    public const string 
        MyValue1 = "My value 1",
        MyValue2 = "My value 2";
}

用法: string MyValue = MyEnumClass.MyValue1;


1
尽管这不是一个枚举,但我认为这可能为用户尝试做的事情提供最佳解决方案。有时,最简单的解决方案是最好的。
Zesty

28

您不能使用枚举来执行此操作,但是可以这样做:

public static class SeparatorChars
{
    public static string Comma = ",";

    public static string Tab = "\t";

    public static string Space = " ";
}

1
+1虽然我认为这是正确的解决方案,但我会更改类的名称或将类型更改为chars。只是为了保持一致。
dowhilefor 2011年

谢谢,您能说出comboBox.DataSource = Enum.GetValues(typeof(myEnum));在这种情况下相当于什么吗?
赛义德·亚兹达尼

1
@ Sean87:我想拥有那个,我会回答JonSkeets的。
Fischermaen 2011年

我认为这几乎是正确的答案,因为它在switch-case块内不可用。该字段应该是const为了。但是如果你愿意的话,它仍然无法帮上忙Enum.GetValues(typeof(myEnum))
安德烈桑塔洛

7
我会用const代替static。常量既是静态的,也是只读的,并且在构造函数中不能赋值(除非只读字段)。
Olivier Jacot-Descombes 2015年

12

您不能这样做,因为枚举只能基于原始数字类型。您可以尝试使用a Dictionary代替:

Dictionary<String, char> separators = new Dictionary<string, char>
{
    {"Comma", ','}, 
    {"Tab",  '\t'}, 
    {"Space", ' '},
};

或者,您可以使用Dictionary<Separator, char>Dictionary<Separator, string>哪里Separator是普通枚举:

enum Separator
{
    Comma,
    Tab,
    Space
}

这比直接处理字符串要舒服一些。


11

可以如下创建模拟枚举行为但使用string代替的类int

public class GrainType
{
    private string _typeKeyWord;

    private GrainType(string typeKeyWord)
    {
        _typeKeyWord = typeKeyWord;
    }

    public override string ToString()
    {
        return _typeKeyWord;
    }

    public static GrainType Wheat = new GrainType("GT_WHEAT");
    public static GrainType Corn = new GrainType("GT_CORN");
    public static GrainType Rice = new GrainType("GT_RICE");
    public static GrainType Barley = new GrainType("GT_BARLEY");

}

用法...

GrainType myGrain = GrainType.Wheat;

PrintGrainKeyword(myGrain);

然后...

public void PrintGrainKeyword(GrainType grain) 
{
    Console.Writeline("My Grain code is " + grain.ToString());   // Displays "My Grain code is GT_WHEAT"
}

例如,唯一的事情是您不能做GrainType myGrain = "GT_CORN"
colmde

如果您忽略了操作员,则可以这样做
SSX-SL33PY

8

答案有点晚了,但也许将来会对某人有所帮助。我发现将struct用于此类问题更容易。

以下示例是从MS代码复制粘贴的部分:

namespace System.IdentityModel.Tokens.Jwt
{
    //
    // Summary:
    //     List of registered claims from different sources http://tools.ietf.org/html/rfc7519#section-4
    //     http://openid.net/specs/openid-connect-core-1_0.html#IDToken
    public struct JwtRegisteredClaimNames
    {
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string Actort = "actort";
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string Typ = "typ";
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string Sub = "sub";
        //
        // Summary:
        //     http://openid.net/specs/openid-connect-frontchannel-1_0.html#OPLogout
        public const string Sid = "sid";
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string Prn = "prn";
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string Nbf = "nbf";
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string Nonce = "nonce";
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string NameId = "nameid";

    }
}

您能否解释一下为什么这种方法比使用课堂更好?
Gerardo Grignoli

@GerardoGrignoli我不完全知道为什么他们在MS中使用struct而不是class。我什至没有试图找出答案,因为这对我来说非常有用。也许尝试在堆栈上问问题...
suchoss 19-10-18

5

对于希望在这里找到更通用问题的答案的人,如果希望代码看起来像,可以扩展静态类的概念enum

当您尚未最终确定所需的时,以下方法将起作用,enum names并且enum values是的string表示enam name;用于nameof()简化您的重构。

public static class Colours
{
    public static string Red => nameof(Red);
    public static string Green => nameof(Green);
    public static string Blue => nameof(Blue);
}

这实现了具有字符串值(例如以下伪代码)的枚举的意图:

public enum Colours
{
    "Red",
    "Green",
    "Blue"
}

5

也许为时已晚,但事情已经过去了。

我们可以使用属性EnumMember来管理Enum值。

public enum EUnitOfMeasure
{
    [EnumMember(Value = "KM")]
    Kilometer,
    [EnumMember(Value = "MI")]
    Miles
}

这样,EUnitOfMeasure的结果值将为KM或MI。这也可以在安德鲁·惠特克的答案中看到。


4

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

用法:

///<completionlist cref="HexColor"/> 
class HexColor : StringEnum<HexColor>
{
    public static readonly HexColor Blue = New("#FF0000");
    public static readonly HexColor Green = New("#00FF00");
    public static readonly HexColor Red = New("#000FF");
}

特征

  • 您的StringEnum看起来与常规枚举类似:
    // 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)
  • 如果该类用xml注释注释,则Intellisense将建议枚举名称<completitionlist>。(适用于C#和VB):

Intellisense演示

安装方式

要么:

  • 安装最新的StringEnum NuGet软件包,该软件包基于,.Net Standard 1.0因此可以在.Net Core> = 1.0,.Net Framework> = 4.5,Mono> = 4.6等上运行。
  • 或将以下StringEnum基类粘贴到您的项目中。(最新版本
    public abstract class StringEnum<T> : IEquatable<T> where T : StringEnum<T>, new()
    {
        protected string Value;
        private static IList<T> valueList = new List<T>();
        protected static T New(string value)
        {
            if (value == null)
                return null; // the null-valued instance is null.

            var result = new T() { Value = value };
            valueList.Add(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 sensitivity.</param>
        public static T Parse(string value, bool caseSensitive = false)
        {
            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 sensitivity.</param>
        public static T TryParse(string value, bool caseSensitive = false)
        {
            if (value == null) return null;
            if (valueList.Count == 0) System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle); // force static fields initialization
            var field = valueList.FirstOrDefault(f => f.Value.Equals(value,
                    caseSensitive ? StringComparison.Ordinal
                                  : StringComparison.OrdinalIgnoreCase));
            // Not using InvariantCulture because it's only supported in NETStandard >= 2.0

            if (field == null)
                return null;

            return field;
        }
    }
  • 为了Newtonsoft.Json支持序列化,请复制此扩展版本。StringEnum.cs

我意识到这个代码类似于Ben的答案。我从头开始真诚地写了它。但是我认为它有一些额外功能,例如<completitionlist>hack,结果类看起来更像枚举,没有在Parse(),NuGet包和repo上使用反射,希望能解决传入的问题和反馈。


3

基于此处的一些答案,我实现了可重用的基类,该基类模仿枚举的行为,但具有string作为基础类型的基类。它支持各种操作,包括:

  1. 获取可能值的列表
  2. 转换为字符串
  3. 与其他实例比较通过.Equals==!=
  4. 使用JSON.NET JsonConverter进行JSON转换

这是全部的基类:

public abstract class StringEnumBase<T> : IEquatable<T>
    where T : StringEnumBase<T>
{
    public string Value { get; }

    protected StringEnumBase(string value) => this.Value = value;

    public override string ToString() => this.Value;

    public static List<T> AsList()
    {
        return typeof(T)
            .GetProperties(BindingFlags.Public | BindingFlags.Static)
            .Where(p => p.PropertyType == typeof(T))
            .Select(p => (T)p.GetValue(null))
            .ToList();
    }

    public static T Parse(string value)
    {
        List<T> all = AsList();

        if (!all.Any(a => a.Value == value))
            throw new InvalidOperationException($"\"{value}\" is not a valid value for the type {typeof(T).Name}");

        return all.Single(a => a.Value == value);
    }

    public bool Equals(T other)
    {
        if (other == null) return false;
        return this.Value == other?.Value;
    }

    public override bool Equals(object obj)
    {
        if (obj == null) return false;
        if (obj is T other) return this.Equals(other);
        return false;
    }

    public override int GetHashCode() => this.Value.GetHashCode();

    public static bool operator ==(StringEnumBase<T> a, StringEnumBase<T> b) => a?.Equals(b) ?? false;

    public static bool operator !=(StringEnumBase<T> a, StringEnumBase<T> b) => !(a?.Equals(b) ?? false);

    public class JsonConverter<T> : Newtonsoft.Json.JsonConverter
        where T : StringEnumBase<T>
    {
        public override bool CanRead => true;

        public override bool CanWrite => true;

        public override bool CanConvert(Type objectType) => ImplementsGeneric(objectType, typeof(StringEnumBase<>));

        private static bool ImplementsGeneric(Type type, Type generic)
        {
            while (type != null)
            {
                if (type.IsGenericType && type.GetGenericTypeDefinition() == generic)
                    return true;

                type = type.BaseType;
            }

            return false;
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            JToken item = JToken.Load(reader);
            string value = item.Value<string>();
            return StringEnumBase<T>.Parse(value);
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            if (value is StringEnumBase<T> v)
                JToken.FromObject(v.Value).WriteTo(writer);
        }
    }
}

这就是实现“字符串枚举”的方式:

[JsonConverter(typeof(JsonConverter<Colour>))]
public class Colour : StringEnumBase<Colour>
{
    private Colour(string value) : base(value) { }

    public static Colour Red => new Colour("red");
    public static Colour Green => new Colour("green");
    public static Colour Blue => new Colour("blue");
}

可以这样使用:

public class Foo
{
    public Colour colour { get; }

    public Foo(Colour colour) => this.colour = colour;

    public bool Bar()
    {
        if (this.colour == Colour.Red || this.colour == Colour.Blue)
            return true;
        else
            return false;
    }
}

我希望有人觉得这有用!


2

好吧,首先,您尝试分配的字符串不是char,即使它们只是一个字符。使用“,”代替“,”。下一件事是,枚举仅采用整数类型,而char您不能使用unicode值,但是我强烈建议您不要这样做。如果您确定这些值在不同的文化和语言中保持不变,那么我将使用带有const字符串的静态类。


2

虽然实际上不可能使用a char或a string作为枚举的基础,但我认为这并不是您真正想要做的。

就像您提到的那样,您希望有一个枚举,并在组合框中显示此字符串的表示形式。如果用户选择这些字符串表示形式之一,则希望获得相应的枚举。这是可能的:

首先,我们必须将一些字符串链接到枚举值。这可以通过使用此处此处DescriptionAttribute所述的类似方法来完成。

现在,您需要创建一个枚举值和相应描述的列表。这可以通过使用以下方法来完成:

/// <summary>
/// Creates an List with all keys and values of a given Enum class
/// </summary>
/// <typeparam name="T">Must be derived from class Enum!</typeparam>
/// <returns>A list of KeyValuePair&lt;Enum, string&gt; with all available
/// names and values of the given Enum.</returns>
public static IList<KeyValuePair<T, string>> ToList<T>() where T : struct
{
    var type = typeof(T);

    if (!type.IsEnum)
    {
        throw new ArgumentException("T must be an enum");
    }

    return (IList<KeyValuePair<T, string>>)
            Enum.GetValues(type)
                .OfType<T>()
                .Select(e =>
                {
                    var asEnum = (Enum)Convert.ChangeType(e, typeof(Enum));
                    return new KeyValuePair<T, string>(e, asEnum.Description());
                })
                .ToArray();
}

现在,您将获得所有枚举的键值对及其描述的列表。因此,让我们简单地将其分配为组合框的数据源。

var comboBox = new ComboBox();
comboBox.ValueMember = "Key"
comboBox.DisplayMember = "Value";
comboBox.DataSource = EnumUtilities.ToList<Separator>();

comboBox.SelectedIndexChanged += (sender, e) =>
{
    var selectedEnum = (Separator)comboBox.SelectedValue;
    MessageBox.Show(selectedEnum.ToString());
}

用户可以看到该枚举的所有字符串表示形式,并且在您的代码中,您将获得所需的枚举值。


0

我们不能将枚举定义为字符串类型。枚举的批准类型为byte,sbyte,short,ushort,int,uint,long或ulong。

如果您需要有关枚举的更多详细信息,请点击以下链接,该链接将帮助您了解枚举。 枚举

@ narendras1414


0

这个对我有用..

   public class ShapeTypes
    {
        private ShapeTypes() { }
        public static string OVAL
        {
            get
            {
                return "ov";
            }
            private set { }
        }

        public static string SQUARE
        {
            get
            {
                return "sq";
            }
            private set { }
        }

        public static string RECTANGLE
        {
            get
            {
                return "rec";
            }
            private set { }
        }
    }

0

我最近开始做的是使用Tuples

public static (string Fox, string Rabbit, string Horse) Animals = ("Fox", "Rabbit", "Horse");
...
public static (string Comma, string Tab, string Space) SeparatorChars = (",", "\t", " ");

-1

灌肠班

 public sealed class GenericDateTimeFormatType
    {

        public static readonly GenericDateTimeFormatType Format1 = new GenericDateTimeFormatType("dd-MM-YYYY");
        public static readonly GenericDateTimeFormatType Format2 = new GenericDateTimeFormatType("dd-MMM-YYYY");

        private GenericDateTimeFormatType(string Format)
        {
            _Value = Format;
        }

        public string _Value { get; private set; }
    }

刺激性消费

public static void Main()
{
       Country A = new Country();

       A.DefaultDateFormat = GenericDateTimeFormatType.Format1;

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