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

我无意中定义了一个今天没有任何价值的枚举。 像这样一个例子:

public enum MyConfusingEnum{} 

编译器非常高兴让我定义这个代码,并成功地构build了代码。

现在我明显不能用传统意义上的代码,

 var mySadCompiler = MyConfusingEnum; 

没有指定一个值,但有趣的是,我可以说,..

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

正如我所暗示的MyConfusingEnum ,它是值为0的MyConfusingEnum的值types;

我的问题是为什么编译器允许一个空的定义,并有任何情况下,它可能是有用的?

首先,你可以做到这一点更容易:

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

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

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

我将从一个问题回答你的问题开始。 你是否也有编译器拒绝?

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

为什么或者为什么不?

为了更直接地不回答你的问题:“为什么世界和现在没有什么不同? 问题很难回答。 我不会回答那个不可能的问题,而是回答这个问题:“假设给devise团队一个错误,你会如何回应这个问题? 这个问题仍然是反事实,但至less是我可以回答的一个问题。

那么这个问题就成了这个function的成本是否由它的好处所certificate的。

为了实现语言function,必须考虑,devise,指定,实施,testing,logging并将其发运给客户。 这是一个“产生错误”的function,所以错误信息必须被写入并翻译成十几种语言。 五分钟的时间,我要实现这个function转化为许多付出相当多的人许多小时的工作。

但是,这实际上并不是相关的成本。 机会成本是相关成本。 预算是有限的,function不是免费的,因此任何实现的function都意味着必须削减其他function。 C#的哪个特性是你想要被剪切来获取这个特性的? 不能做更好的function所带来的损失就是机会成本

我还注意到,你提出的function对任何人没有明显的好处 ,这对devise委员会来说是一个难题。 也许有一个令人信服的好处,我没有看到; 如果是的话,那是什么?

有没有可能有用的场景?

没有人想到。 “拒绝不明显的程序”并不是C#的devise目标。

您可以将任何基础整数types的值(我认为默认情况下为int )转换为枚举 – 现在(MyConfusingEnum)42将成为该枚举types。

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

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

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

规范确实允许空的枚举:

14.1枚举声明

枚举声明声明一个新的枚举types。 枚举声明以关键字enum开头,并定义了枚举的名称,可访问性,底层types和成员。

 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)明确标记为variant,其中{}内没有任何内容。

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

调用一个枚举的构造函数会给你0的值。

由于devise决定,枚举可以有任何有效的支持types的值(通常是int ),它不一定是在枚举中定义的值。

由于这个devise决定的原因,我可以指出你对这个问题的答案题为“为什么铸造int无效枚举值不抛出exception?

@AlexeiLevenkov提供了允许一个空枚举的规范,我们可以猜测这个理由是因为任何后备types的值是有效的,允许一个空的枚举。

有没有可能有用的场景?

正如其他人已经提到你可以分配给这个枚举的任何价值,基本types允许通过一个简单的演员。 这样,你可以强制types检查,在一个int会使事情混乱的情况下。 例如:

 public enum Argb : int {} public void SetColor(Argb a) { .... 

或者你想拥有一些扩展方法,而不需要使用int数据types

 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; 

有没有可能有用的场景?

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

在枚举的情况下, 您可以在编译时知道所有可能的值 。

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

在devise一个API的过程中,我实现了一个没有任何值的枚举来引用它从其他接口。 我稍后添加了值。