为什么只有整体枚举?

时间:2010-06-04 00:19:20

标签: c# string enums

我已经写了七年C#,我一直在想,为什么枚举必须是一个完整的类型?做一些像这样的事情不是很好:

enum ErrorMessage 
{ 
     NotFound: "Could not find",
     BadRequest: "Malformed request"
}

这是一种语言设计选择,还是在编译器,CLR或IL级别上存在基本的不兼容性?

其他语言是否包含字符串或复杂(即对象)类型的枚举?什么语言?

(我知道解决方法;我的问题是,为什么需要?)

编辑:“workarounds”=带有consts的属性或静态类:)

10 个答案:

答案 0 :(得分:8)

Enum的目的是为整数提供更有意义的值。你正在寻找Enum以外的其他东西。枚举与旧的Windows API和COM东西兼容,并且在其他平台上也有很长的历史。

也许你对结构或类的公共const成员感到满意。

或者您可能尝试将某些特殊类型值限制为仅某些字符串值?但它如何存储以及如何显示它可能是两个不同的东西 - 为什么要使用比存储值更多的空间?

如果你想在某种持久化格式中使用可读的东西,只需使用实用程序或扩展方法将其吐出。

这种反应有点乱,因为原因很多。比较两个字符串的有效性比比较两个整数要贵得多。将文字字符串与已知的枚举进行比较以进行静态类型检查有点不合理。本地化会......很奇怪。兼容性将被打破。作为标志的枚举将毫无意义/破坏。

这是一个枚举。这就是Enums所做的!他们是不可或缺的!

答案 1 :(得分:7)

也许使用System.ComponentModel中的description属性并编写一个辅助函数来从枚举值中检索关联的字符串? (我在我使用的代码库中看到了这个,似乎是一个非常合理的选择)

enum ErrorMessage 
{ 
     [Description("Could not find")]
     NotFound,
     [Description("Malformed request")]
     BadRequest
}

答案 2 :(得分:6)

有什么好处,因为我只能看到缺点:

  • ToString将返回与枚举名称不同的字符串。也就是说,ErrorMessage.NotFound.ToString()将是“找不到”而不是“NotFound”。
  • 相反,Enum.Parse,它会做什么?它是否仍然接受枚举的字符串名称,就像它对整数枚举一样,还是使用字符串 value
  • 你将无法实现[Flags],因为在你的例子中ErrorMessage.NotFound | ErrorMessage.BadRequest相等(我知道在这种特殊情况下它没有意义,我想你可以说基于字符串的枚举不允许[Flags],但这对我来说似乎仍然是一个缺点)
  • 虽然比较errMsg == ErrorMessage.NotFound可以作为简单的参考比较实现,但errMsg == "Could not find"需要实现为字符串比较。

我无法想到任何好处,特别是因为构建自己的字典映射枚举值到“自定义”字符串非常容易。

答案 3 :(得分:4)

真正的答案原因是:从来没有令人信服的理由让枚举变得比现在复杂得多。如果你需要一个简单的闭合值列表 - 它们就是它。

在.Net中,枚举被赋予了internal representation< - >的额外好处。 the string used to define them。这一个小小的改变增加了一些版本化的缺点,但在C ++中的枚举上有所改进。

  

enum关键字用于声明   枚举,一种独特的类型   由一组命名常量组成   称为枚举器列表。

参考:msdn

您的问题是使用所选的存储机制,即整数。这只是一个实现细节。我们只是在这个简单类型的封面下偷看,以保持二进制兼容性。否则,枚举的用处非常有限。

问:为什么枚举使用整数存储?正如其他人指出的那样:

  1. 整数可以快速轻松地进行比较。
  2. 整数快速且易于组合([Flags]样式枚举的按位)
  3. 使用整数,实现枚举非常容易。
  4. *这些都不是特定于.net的,看起来CLR设计师显然没有被迫改变任何东西或添加任何镀金。

    现在不是说你的语法并不完全没有吸引力。但是,在CLR中实现此功能的努力以及所有编译器是否合理?对于所有的工作,它真的给你带来了你无法实现的任何东西(有课程)吗?我的直觉不是,没有真正的好处。 (有Eric Lippert的帖子,我想链接到,但我找不到它)

    你可以编写10行代码来在用户空间中实现你想要实现的目标,而不必担心更改编译器。随着时间的推移,您的用户空间代码很容易维护 - 虽然可能不像内置的那么漂亮,但在一天结束时它也是一样的。如果您需要在项目中维护许多自定义枚举值,您甚至可以使用T4 code generation template

    因此,枚举就像它们需要的那样复杂。


答案 4 :(得分:2)

并没有真正回答你的问题,而是提供字符串枚举的替代品。

public struct ErrorMessage  
{  
     public const string NotFound="Could not find";
     public const string BadRequest="Malformed request";
} 

答案 5 :(得分:1)

也许是因为那时没有意义:

enum ErrorMessage: string
{
    NotFound,
    BadRequest
}

答案 6 :(得分:1)

这是一种语言决定 - 例如,Java's enum doesn't directly correspond to an int, but is instead an actual class。 int enum为你提供了很多很好的技巧 - 你可以将它们用于标记,迭代它们(通过加1或减1)等等。但是,它也有一些缺点 - 缺少额外的元数据,任何元数据int为无效值,等等。

我认为可能做出的决定与大多数设计决策一样,因为int enums“足够好”。如果你需要更复杂的东西,那么课程便宜且易于构建。

静态只读成员会为您提供复杂枚举的效果,但除非您需要,否则不会产生开销。

 static class ErrorMessage {
     public string Description { get; private set; }
     public int Ordinal { get; private set; }
     private ComplexEnum() { }

     public static readonly NotFound = new ErrorMessage() { 
         Ordinal = 0, Description = "Could not find" 
     };
     public static readonly BadRequest = new ErrorMessage() { 
         Ordinal = 1, Description = "Malformed Request" 
     };
 }

答案 7 :(得分:1)

严格地说,枚举的内在表示无关紧要,因为根据定义,它们是枚举类型。这意味着什么

public enum PrimaryColor { Red, Blue, Yellow }

表示一组值。

首先,一些集较小,而其他集较大。因此,.NET CLR允许基于整数类型的枚举,以便枚举值的域大小可以增加或减少,即,如果枚举基于一个字节,那么该枚举不能包含超过256不同的值,而基于long的值可以包含2 ^ 64个不同的值。这是因为long是一个字节的8倍。

其次,将枚举的基本类型限制为整数值的另一个好处是,可以对枚举值执行按位运算,并创建它们的位图以表示多个值。

最后,整数类型是计算机内可用的最有效的数据类型,因此,在比较不同的枚举值时,存在性能优势。

在大多数情况下,我会说用整数类型表示枚举似乎是CLR和/或CLS设计选择,尽管可能不是很难达到。

答案 8 :(得分:0)

整体枚举的主要优点是它们不会在内存中占用太多空间。默认的System.Int32支持的枚举实例只占用4个字节的内存,可以快速与该枚举的其他实例进行比较。

相反,字符串支持的枚举将是需要在堆上分配每个实例的引用类型,并且比较涉及检查字符串中的每个字符。您可以在运行时和编译器中尽量减少一些创造性问题,但在尝试将枚举有效地存储在数据库或其他外部存储中时,您仍会遇到类似的问题。

答案 9 :(得分:0)

虽然它也算作“替代品”,但你仍然可以做得更好,而不仅仅是一堆内容:

struct ErrorMessage
{
    public static readonly ErrorMessage NotFound =
        new ErrorMessage("Could not find");
    public static readonly ErrorMessage BadRequest =
        new ErrorMessage("Bad request");

    private string s;

    private ErrorMessage(string s)
    {
        this.s = s;
    }

    public static explicit operator ErrorMessage(string s)
    {
        return new ErrorMessage(s);
    }

    public static explicit operator string(ErrorMessage em)
    {
        return em.s;
    }
}

这里唯一的问题是,作为任何值类型,这个值都有一个默认值,它将具有s==null。但这与Java枚举没有什么不同,Java枚举本身可以是null(作为引用类型)。

通常,类似Java的高级枚举跨越实际枚举之间的界限,以及密封类层次结构的语法糖。这种糖是否是一个好主意是有争议的。