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

我今天不小心定义了一个不包含任何值的枚举:

public enum MyConfusingEnum{}

编译器很高兴让我定义它,并且成功地构建了代码。

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

var mySadCompiler = MyConfusingEnum;

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

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

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

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

4966 次浏览

您可以将任何基础整数类型的值(我认为缺省情况下是 int)强制转换为枚举-因此 (MyConfusingEnum)42现在将为该枚举类型。

我不认为这是一个好主意,但是有时候“ enum”值来自外部源代码,而代码使用 enum看起来更好。

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

enum ExternalDeviceState {};


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

规范确实允许空枚举:

14.1枚举声明

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

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)显式标记为变体,其中 {}中没有任何内容。

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

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

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

由于这个设计决策的原因,我可以向你指出 这个答案的一个题目为 “为什么将 int 转换为无效枚举值不会引发异常?”的问题

@ AlexeiLevenkov 已经提供了允许空枚举的规范,我们可以猜测其基本原理是,由于任何支持类型的值都是有效的,所以允许空枚举。

首先,你可以更容易地完成 很多:

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

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

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

首先,我用一个问题来回答你的问题。你也会让编译器拒绝吗?

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

为什么?

更直接地不回答你的问题: “为什么世界与现在没有什么不同?”问题很难回答。与其回答这个不可能的问题,我将回答这个问题: “假设向设计团队推销空枚举是一个错误; 你会如何回应这个推销?”这个问题仍然是 反事实,但至少我可以回答。

然后问题就变成了特征的 成本是否由它的 福利来证明。

为了工作语言特性必须思考、设计、指定、实现、测试、记录并发送给客户。这是一个“生成错误”特性,因此必须将错误消息编写并翻译成几十种语言,文档也是如此。实现这个特性所花费的五分钟转化为许多人的许多工作时间,而这些人的工资相当高。

然而,这实际上并不是相关的成本。机会成本是相关的成本。预算是有限的,特性不是免费的,因此任何实现的特性都意味着一些其他的特性必须被删除; 你希望 C # 的哪个特性被删除以得到这个特性?迷失了受益于 没有能够做一个更好的功能是 机会成本

我还注意到,你提出的功能有 对任何人都没有明显的好处,这将使它很难推销给设计委员会。也许有一个令人信服的好处,我没有看到; 如果是这样,它是什么?

有没有什么情况下它会有用呢?

“拒绝没有明显用处的程序”不是 C # 的设计目标。

有没有什么情况下它会有用呢?

正如其他人已经提到的,您可以通过简单的强制转换为基础类型允许的任何值赋值给此枚举。 这样你就可以强制执行类型检查,在 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;

有没有什么情况下它会有用?

我在爪哇世界经历过一次。

如果是枚举,则为 在编译时你知道所有可能的值

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

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