为什么C#编译器允许空枚举?

时间:2014-05-21 22:30:43

标签: c# compiler-construction enums

我今天意外地定义了一个不含值的枚举。比如这个例如:

public enum MyConfusingEnum{}

编译器很高兴让我定义并成功构建代码。

现在我显然不能使用传统意义上的代码,因为代码,..

var mySadCompiler = MyConfusingEnum;

没有指定一个值,但有趣的是我 能够说,..

var myRoundTheHousesZeroState = Activator.CreateInstance<MyConfusingEnum>();

,正如我所提到的,是MyConfusingEnum的值类型,值为0;

我的问题是为什么编译器允许一个空的定义,是否有任何可能有用的场景?

5 个答案:

答案 0 :(得分:91)

首先,您可以更轻松地完成更多

MyConfusingEnum x1 = 0;
MyConfusingEnum x2 = default(MyConfusingEnum);
MyConfusingEnum x3 = new MyConfusingEnum();
MyConfusingEnum x4 = (MyConfusingEnum) 123;

以上所有工作都很好。 (您可能会对第一个工作感到惊讶;有关详细信息,请参阅隐式枚举转换的规范部分。)

  

我的问题是为什么编译器允许空定义

我首先回答一个问题。你还有编译器拒绝吗?

class C {}
interface I {}
struct S {}

为什么或为什么不呢?

更直接地不回答你的问题:&#34;为什么这个世界与它没有什么不同?&#34;问题很难回答。我没有回答那个不可能的问题,而是回答了这个问题&#34;假设制作空的枚举错误已经被提交给设计团队;你会如何回应这个音调?&#34;这个问题仍然是反事实,但至少它是我能回答的问题。

然后问题是该功能的成本是否由其好处证明是合理的。

为了工作,语言功能必须被考虑,设计,指定,实施,测试,记录并发送给客户。这是一个&#34;产生错误&#34;功能,因此必须编写错误消息并将其翻译成几十种语言,文档也必须如此。实施该功能所花费的五分钟就可以转化为许多人付出相当多的工作时间。

然而,这实际上并不是相关成本。 机会成本是相关费用。预算是有限的,功能不是免费的,因此任何实现的功能都意味着必须削减一些其他功能;您希望C#的哪个功能被剪切以获得此功能? 能够做出更好的功能的丢失好处是机会成本

我还注意到你提议的功能对任何人来说都没有明显的好处,这会让设计委员会难以卖出。也许我没有看到令人信服的好处;如果是的话,它是什么?

  

是否有任何可能有用的场景?

没有想到的。 &#34;拒绝不明显有用的程序&#34;不是C#的设计目标。

答案 1 :(得分:17)

您可以将任何基础整数类型的值(我认为默认为int)强制转换为枚举 - 因此(MyConfusingEnum)42现在将属于该枚举类型。

我认为这不是一个好主意,但可能会出现“枚举”值来自外部来源并且代码看起来更好enum的情况。

示例(假设代码在Enum中填充了一些“基于int的状态”:

enum ExternalDeviceState {};

ExternalDeviceState GetState(){ ... return (ExternalDeviceState )intState;}
bool IsDeviceStillOk(ExternalDeviceState currentState) { .... }

规范确实允许空枚举:

  

14.1枚举声明

     

枚举声明声明一个新的枚举类型。枚举声明以关键字enum开头,并定义枚举的名称,可访问性,基础类型和成员。

enum-declaration:
   attributesopt   enum-modifiersopt   enum   identifier
        enum-base(opt)   enum-body   ;(opt)

enum-base:
:   integral-type

enum-body:
  {   enum-member-declarations(opt)   }  
  {   enum-member-declarations   ,   }

请注意enum-member-declarations(opt)被明确标记为变体,{}内没有任何内容。

答案 2 :(得分:14)

Activator.CreateInstance<MyConfusingEnum>();new MyConfusingEnum()相同。 (docs

调用枚举的构造函数会将0作为值。

由于设计决策,枚举可以包含任何对支持类型有效的值(通常为int),它不必是枚举中定义的值。

出于设计决策的原因,我可以针对标题为的问题指出this answer“为什么将int转换为无效的枚举值而不是抛出异常?”

@AlexeiLevenkov提供了允许空枚举的规范,我们可以猜测,这是因为任何支持类型值都有效,所以允许空枚举。

答案 3 :(得分:7)

  

是否有任何可能有用的场景?

正如其他人已经提到的那样,您可以通过简单的强制转换将此基础类型允许的任何值分配给此枚举。 这样,在int会使事情混乱的情况下,您可以强制执行类型检查。例如:

public enum Argb : int {}

public void SetColor(Argb a) { ....

或者你想要一些扩展方法,而不是使用它们来填充int数据类型

public static Color GetColor(this Argb value) {
    return new Color( (int)value );
}

public static void Deconstruct( this Argb color, out byte alpha, out byte red, out byte green, out byte blue ) {
        alpha = (byte)( (uint)color >> 24 );
        red = (byte)( (uint)color >> 16 );
        green = (byte)( (uint)color >> 8 );
        blue = (byte)color;
}

并将其用作

var (alpha, red, green, blue) = color;

答案 4 :(得分:3)

  
    

是否有任何可能有用的场景?

  

我在Java世界中经历过一次。

  
    

如果是枚举,you know all possible values at compile time

  

但是,在编译时间之前有一段时间你可能不知道所有的值(还是),或者你根本不打算实现任何值。

在设计API的过程中,我实现了一个没有任何值的枚举,以便能够从其他接口引用它。我以后加了价值。