为什么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的过程中,我实现了一个没有任何值的枚举来引用它从其他接口。 我稍后添加了值。