将枚举与C#中的字符串相关联

Bor*_*ens 298 .net c#

我知道以下是不可能的,因为它必须是一个int

enum GroupTypes
{
    TheGroup = "OEM",
    TheOtherGroup = "CMB"
}
Run Code Online (Sandbox Code Playgroud)

从我的数据库中我得到一个包含不全面代码的字段(OEM和CMB).我想把这个领域变成一个枚举或其他可以理解的东西.因为目标是可读性,所以解决方案应该简洁.
我还有其他选择吗?

Eve*_*ien 341

我喜欢在类中使用属性而不是方法,因为它们看起来更像枚举.

以下是Logger的示例:

public class LogCategory
{
    private LogCategory(string value) { Value = value; }

    public string Value { get; set; }

    public static LogCategory Trace   { get { return new LogCategory("Trace"); } }
    public static LogCategory Debug   { get { return new LogCategory("Debug"); } }
    public static LogCategory Info    { get { return new LogCategory("Info"); } }
    public static LogCategory Warning { get { return new LogCategory("Warning"); } }
    public static LogCategory Error   { get { return new LogCategory("Error"); } }
}
Run Code Online (Sandbox Code Playgroud)

类型安全的字符串值作为参数传递:

public static void Write(string message, LogCategory logCategory)
{
    var log = new LogEntry { Message = message };
    Logger.Write(log, logCategory.Value);
}
Run Code Online (Sandbox Code Playgroud)

用法:

Logger.Write("This is almost like an enum.", LogCategory.Info);
Run Code Online (Sandbox Code Playgroud)

  • 为了我自己的使用,我扩展了这个概念,重写了`ToString`方法以返回`Value`.然后在字符串中提供隐式转换运算符.`public static implicit operator String(LogCategory category){返回值; }`. (12认同)
  • 在交换机箱中使用它怎么样? (5认同)
  • 我唯一能想到的是,它会慢一点,但在大多数情况下这可以忽略不计.它在编辑器中不会有完全相同的行为.EG:切换这个不会自动填写每种可能性的情况.除了那些小问题,我认为这可能是一个相当简单的解决方案. (4认同)
  • @ArnisL.仅作为键工作是不够的,您需要重写Equals()和GetHashCode(),并且您希望将Value属性设置器设为私有.不过,这不是一个枚举. (4认同)
  • 缺点是函数参数中没有用作默认值: public string GetLogs(LogCategory cat = LogCategory.Trace) 将返回错误“默认参数值必须是编译时常量”。 (4认同)
  • 并且很容易使用Dictionary <LogCategory,Action/Func>作为开关.:) (3认同)
  • @НиколаКараклић你是绝对正确的。这些静态属性应该已自动初始化。 (3认同)
  • 明智的决定。我将“值”设置器设为私有。 (2认同)
  • 现在使用c#6,您可以使它更加整洁,`public static string Trace =&gt;“ Trace”;`。 (2认同)
  • @ghanashyaml 因为这不适用于带空格的字符串,或者如果您想要与枚举不同的大小写。 (2认同)
  • @RogerSampaio 由于您要返回字符串,因此不能将该类用作参数类型 (2认同)
  • 我想指出的是,此实现将消耗更多内存(因为每次访问属性时都会创建一个新对象),特别是如果您经常使用这些属性。 (2认同)
  • 如何使用此解决方案来反序列化具有 LogCategoryType 属性的 json? (2认同)
  • 更好 - 使用静态构造函数: ```public class GroupType { static GroupType() { TheGroup = new GroupType("OEM"); TheOtherGroup = new GroupType("CMB"); } 私有 GroupType(字符串名称) { 名称 = 名称; } 公共静态 GroupType TheGroup { 获取;} 公共静态 GroupType TheOtherGroup { 获取;} 公共字符串名称 { get; } }``` 保留相等性而不覆盖 `.Equals` 并维护每个值的单例,避免不必要的实例化和内存开销。 (2认同)
  • @Evan Mien,让静态属性确实只能获取,但具有适当的存储值不是更好吗?`公共静态LogCategory Trace { get; } = new("Trace");` 这样 Trace 的每个实例(以及其他实例)都将是相同的引用。另外,这解决了@Никола Караклић的上述担忧 (2认同)

Gle*_*lar 154

您还可以使用扩展模型:

public enum MyEnum
{
    [Description("String 1")]
    V1= 1,
    [Description("String 2")]
    V2= 2
} 
Run Code Online (Sandbox Code Playgroud)

您的扩展类

public static class MyEnumExtensions
{
    public static string ToDescriptionString(this MyEnum val)
    {
        DescriptionAttribute[] attributes = (DescriptionAttribute[])val
           .GetType()
           .GetField(val.ToString())
           .GetCustomAttributes(typeof(DescriptionAttribute), false);
        return attributes.Length > 0 ? attributes[0].Description : string.Empty;
    }
} 
Run Code Online (Sandbox Code Playgroud)

用法:

MyEnum myLocal = MyEnum.V1;
print(myLocal.ToDescriptionString());
Run Code Online (Sandbox Code Playgroud)

  • 我不禁要想,从性能的角度来看,每次要显示枚举时都要反映枚举听起来有些痛苦! (5认同)
  • 另请参阅http://stackoverflow.com/questions/4367723/get-enum-from-description-attribute以获取其他扩展名,并通过描述从字符串到enum. (3认同)
  • @Liath - `.ToString()`已经使用了反射,因此你不会因为这种方法而失去任何东西,并获得可读性 (3认同)
  • 为了使通用,使用`public static string ToDescriptionString(this Enum ...`,即不显式键入`MyEnum`。 (2认同)
  • 这需要`using System.ComponentModel;` (2认同)

rpa*_*abi 83

如何使用带常量的静态类?客户端代码看起来与枚举没有什么不同.

static class GroupTypes
{
  public const string TheGroup = "OEM";
  public const string TheOtherGroup = "CMB";
}

void DoSomething(string groupType)
{
  if(groupType == GroupTypes.TheGroup)
  {
    // Be nice
  }  
  else if (groupType == GroupTypes.TheOtherGroup)
  {
    // Continue to be nice
  }
  else
  {
    // unexpected, throw exception?
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 静态类型不能用作参数. (38认同)
  • 同意.我无法看到更复杂的解决方案背后的目的,除非能够切换生成的"枚举". (9认同)
  • 正如@PedroMoreira 指出的那样,您不能将 `GroupTypes` 作为参数类型传递,因为它是一个静态类。这就是 Even Mien 的答案解决的问题。在这种情况下,您必须拥有`void DoSomething(string groupType)`,这意味着`groupType` 可以具有**任何字符串值**,甚至是您不期望的值,这意味着您有为那些无效类型做好准备并决定如何处理它们(例如通过抛出异常)。即使是 Mien 的答案也通过限制“LogCategory”类定义的选项的有效输入数量来解决这个问题。 (7认同)
  • 这些常量需要是内部的或公共的才能实现 (2认同)

cas*_*One 28

您可以向枚举中的项添加属性,然后使用反射从属性中获取值.

您必须使用"field"说明符来应用属性,如下所示:

enum GroupTypes
{
    [field:Description("OEM")]
    TheGroup,

    [field:Description("CMB")]
    TheOtherGroup
}
Run Code Online (Sandbox Code Playgroud)

然后,您将反映枚举类型的静态字段(在本例中为GroupTypes),并DescriptionAttribute使用反射获取您要查找的值:

public static DescriptionAttribute GetEnumDescriptionAttribute<T>(
    this T value) where T : struct
{
    // The type of the enum, it will be reused.
    Type type = typeof(T);

    // If T is not an enum, get out.
    if (!type.IsEnum) 
        throw new InvalidOperationException(
            "The type parameter T must be an enum type.");

    // If the value isn't defined throw an exception.
    if (!Enum.IsDefined(type, value))
        throw new InvalidEnumArgumentException(
            "value", Convert.ToInt32(value), type);

    // Get the static field for the value.
    FieldInfo fi = type.GetField(value.ToString(), 
        BindingFlags.Static | BindingFlags.Public);

    // Get the description attribute, if there is one.
    return fi.GetCustomAttributes(typeof(DescriptionAttribute), true).
        Cast<DescriptionAttribute>().SingleOrDefault();
}
Run Code Online (Sandbox Code Playgroud)

DescriptionAttribute如果您希望能够确定属性是否应用,我选择返回上面的内容.


小智 24

我使用了先前答案中提到的结构,但消除了任何复杂性。对我来说,这最像创建一个字符串枚举。它的使用方式与使用枚举的方式相同。

    struct ViewTypes
    {
        public const string View1 = "Whatever string you like";
        public const string View2 = "another string";
    }
Run Code Online (Sandbox Code Playgroud)

使用示例:

   switch( some_string_variable )
   {
      case ViewTypes.View1: /* do something */ break;
      case ViewTypes.View2: /* do something else */ break;
   }
Run Code Online (Sandbox Code Playgroud)

  • 在给出的所有答案中,我认为这是最好的解决方案。几乎所有的答案都试图硬塞一个解决方案,但它们增加了比应有的复杂性。有时,使用不同的解决方案或数据结构比强迫某些东西按照您认为应该的方式工作更好。我个人使用这样的结构而不是枚举,它使我的代码更具可读性和可维护性。 (3认同)

小智 22

你可以很容易地做到这一点.使用以下代码.

enum GroupTypes
{
   OEM,
   CMB
};
Run Code Online (Sandbox Code Playgroud)

然后,当您想要获取每个枚举元素的字符串值时,只需使用以下代码行.

String oemString = Enum.GetName(typeof(GroupTypes), GroupTypes.OEM);
Run Code Online (Sandbox Code Playgroud)

我以前成功地使用过这个方法,并且我还使用了一个常量类来保存​​字符串常量,两者都运行得很好,但我更喜欢这个.

  • 不,这只是获取枚举值的名称,而不是将字符串赋值给枚举值.OP的目标是使字符串与枚举值不同,例如:TheGroup ="OEM",TheOtherGroup ="CMB". (11认同)
  • 我同意@Tim的评论,这不是*OP正在尝试做什么.如果您想知道这是一个什么用例,请考虑设备将字符串作为命令的情况,但也需要一个"人类可读"的命令版本.我需要将"更新固件"之类的内容与"UPDATEFW"命令联系起来. (3认同)

dar*_*asd 17

尝试将常量添加到静态类.你没有最终得到一个类型,但你有可读的,有组织的常量:

public static class GroupTypes {

    public const string TheGroup = "OEM";
    public const string TheOtherGroup = "CMB";

}
Run Code Online (Sandbox Code Playgroud)

  • 问题是它不能作为枚举工作,因为我们不会有一个带有有限值列表的单独类型。期望这些的函数可以与容易出错的自由格式字符串一起使用。 (3认同)
  • 很难从代码返回到描述性名称。您将必须在所有const字段上使用反射来搜索匹配项。 (2认同)

Dav*_*nde 13

为包含以下内容的数据库创建第二个枚举:

enum DBGroupTypes
{
    OEM = 0,
    CMB = 1
}
Run Code Online (Sandbox Code Playgroud)

现在,您可以使用Enum.Parse从字符串"OEM"和"CMB"中检索正确的DBGroupTypes值.然后,您可以将这些转换为int,并从您希望在模型中进一步使用的右枚举中检索正确的值.

  • 与使用属性和反射相反? (11认同)

C. *_*oss 13

使用课程.

编辑:更好的例子

class StarshipType
{
    private string _Name;
    private static List<StarshipType> _StarshipTypes = new List<StarshipType>();

    public static readonly StarshipType Ultralight = new StarshipType("Ultralight");
    public static readonly StarshipType Light = new StarshipType("Light");
    public static readonly StarshipType Mediumweight = new StarshipType("Mediumweight");
    public static readonly StarshipType Heavy = new StarshipType("Heavy");
    public static readonly StarshipType Superheavy = new StarshipType("Superheavy");

    public string Name
    {
        get { return _Name; }
        private set { _Name = value; }
    }

    public static IList<StarshipType> StarshipTypes
    {
        get { return _StarshipTypes; }
    }

    private StarshipType(string name, int systemRatio)
    {
        Name = name;
        _StarshipTypes.Add(this);
    }

    public static StarshipType Parse(string toParse)
    {
        foreach (StarshipType s in StarshipTypes)
        {
            if (toParse == s.Name)
                return s;
        }
        throw new FormatException("Could not parse string.");
    }
}
Run Code Online (Sandbox Code Playgroud)


use*_*664 10

.Net Core 3.0/C# 8.0中的新功能(如果您的工作环境允许您升级项目)是一个看起来有点枚举的简写 switch 语句。归根结底,这还是我们多年来一直使用的旧的、无聊的 switch 语句。

这里唯一真正的区别是 switch 语句有了新的套装。

public static RGBColor FromRainbow(Rainbow colorBand) =>
colorBand switch
{
    Rainbow.Red    => new RGBColor(0xFF, 0x00, 0x00),
    Rainbow.Orange => new RGBColor(0xFF, 0x7F, 0x00),
    Rainbow.Yellow => new RGBColor(0xFF, 0xFF, 0x00),
    Rainbow.Green  => new RGBColor(0x00, 0xFF, 0x00),
    Rainbow.Blue   => new RGBColor(0x00, 0x00, 0xFF),
    Rainbow.Indigo => new RGBColor(0x4B, 0x00, 0x82),
    Rainbow.Violet => new RGBColor(0x94, 0x00, 0xD3),
    _              => throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand)),
};
Run Code Online (Sandbox Code Playgroud)

您会注意到,我从此处复制的上面的代码实际上使用枚举作为参数。

这并不完全是你想要的(相信我,我长期以来一直想要类似于OP所要求的东西),但我实际上觉得这在某种程度上是来自MS的橄榄枝。JMO。

希望它能帮助别人!


mol*_*ins 7

摘自@EvenMien 并添加了一些评论。(也适合我自己的用例)

public struct AgentAction
{
    private AgentAction(string value) { Value = value; }

    public string Value { get; private set; }

    public override string ToString() { return this.Value; }
    
    public static AgentAction Login = new AgentAction("Login");
    public static AgentAction Logout = new AgentAction("Logout");

    public static implicit operator string(AgentAction action) { return action.ToString(); }
}
Run Code Online (Sandbox Code Playgroud)


aza*_*arp 6

这是我用来将枚举值作为字符串的扩展方法.首先是枚举.

public enum DatabaseEnvironment
{
    [Description("AzamSharpBlogDevDatabase")]
    Development = 1, 
    [Description("AzamSharpBlogQADatabase")]
    QualityAssurance = 2, 
    [Description("AzamSharpBlogTestDatabase")] 
    Test = 3
}
Run Code Online (Sandbox Code Playgroud)

Description属性来自System.ComponentModel.

这是我的扩展方法:

public static string GetValueAsString(this DatabaseEnvironment environment) 
{
    // get the field 
    var field = environment.GetType().GetField(environment.ToString());
    var customAttributes = field.GetCustomAttributes(typeof (DescriptionAttribute), false);

    if(customAttributes.Length > 0)
    {
        return (customAttributes[0] as DescriptionAttribute).Description;  
    }
    else
    {
        return environment.ToString(); 
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,您可以使用以下代码访问枚举作为字符串值:

[TestFixture]
public class when_getting_value_of_enum
{
    [Test]
    public void should_get_the_value_as_string()
    {
        Assert.AreEqual("AzamSharpBlogTestDatabase",DatabaseEnvironment.Test.GetValueAsString());  
    }
}
Run Code Online (Sandbox Code Playgroud)


Lui*_*tes 6

处理问题的另一种方法是使用枚举和字符串数组,将枚举值与字符串列表进行映射:

public enum GroupTypes
{
    TheGroup  = 0,
    TheOtherGroup 
}

string[] GroupTypesStr = {
    "OEM",
    "CMB"
};
Run Code Online (Sandbox Code Playgroud)

你可以使用这样的东西:

Log.Write(GroupTypesStr[(int)GroupTypes.TheOtherGroup]);
Run Code Online (Sandbox Code Playgroud)

它会提示CMB

优点:

  1. 简单而干净的代码.
  2. 高性能(特别是与那些使用类的方法相比)

缺点:

  1. 在编辑时容易搞乱列表,但是对于一个简短的列表是可以的.


Ger*_*lez 6

为什么不直接使用相同的枚举,而只是调用.ToString()

using System;

public class EnumSample
{
    enum Holidays
    {
        Christmas = 1,
        Easter = 2
    };

    public static void Main()
    {
        Enum myHolidays = Holidays.Christmas;
        Console.WriteLine("The value of this instance is '{0}'", myHolidays.ToString());
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 当显示文本没有空格时,这是最好的答案。如果需要,您也可以使用辅助函数将帕斯卡大小写转换为带空格的单词。 (2认同)

Jim*_*hel 5

您是否考虑过使用字典的查找表?

enum GroupTypes
{
    TheGroup,
    TheOtherGroup
}

Dictionary<string, GroupTypes> GroupTypeLookup = new Dictionary<string, GroupTypes>();
// initialize lookup table:
GroupTypeLookup.Add("OEM", TheGroup);
GroupTypeLookup.Add("CMB", TheOtherGroup);
Run Code Online (Sandbox Code Playgroud)

然后,您可以在读取字符串时使用 GroupTypeLookup.TryGetValue() 来查找字符串。