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

Eig*_*ite 73 c# compiler-construction enums

我今天意外地定义了一个没有值的枚举.像这样的例如:

public enum MyConfusingEnum{}
Run Code Online (Sandbox Code Playgroud)

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

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

var mySadCompiler = MyConfusingEnum;
Run Code Online (Sandbox Code Playgroud)

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

var myRoundTheHousesZeroState = Activator.CreateInstance<MyConfusingEnum>();
Run Code Online (Sandbox Code Playgroud)

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

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

Eri*_*ert 91

首先,您可以更轻松地完成这项工作:

MyConfusingEnum x1 = 0;
MyConfusingEnum x2 = default(MyConfusingEnum);
MyConfusingEnum x3 = new MyConfusingEnum();
MyConfusingEnum x4 = (MyConfusingEnum) 123;
Run Code Online (Sandbox Code Playgroud)

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

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

我将首先回答你的问题.你还有编译器拒绝吗?

class C {}
interface I {}
struct S {}
Run Code Online (Sandbox Code Playgroud)

为什么或者为什么不?

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

然后问题就在于特征的成本是否因其好处而合理.

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

但是,这实际上并不是相关成本.的机会成本是相关成本.预算是有限的,功能不是免费的,因此任何实现的功能都意味着必须削减一些其他功能; 您希望C#的哪个功能被剪切以获得此功能?在失去从中获益能够做一个更好的特点是机会成本.

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

有什么场景可以有用吗?

没有人想到."拒绝不明显有用的程序"不是C#的设计目标.

  • 很好的答案.**tl; dr:**"拒绝不明显有用的程序不是C#的设计目标." (25认同)
  • 令人困惑的是,VB.NET不允许你声明一个空的枚举!! (12认同)
  • @Alvaro:很有意思; 我不知道.如果你很好奇,你将不得不问VB团队为什么会这样,因为我不知道该功能的理由是什么.再说一遍,VB有很多奇怪的小功能; 它在历史上有更多...我们应该说*自由主义*态度,是的,*自由主义*是好的...增加许多小功能. (12认同)
  • 只是一个注释:谈论空接口 - >标记接口 (7认同)
  • @SilviuBurcea:Microsoft建议使用Attributes,而不是标记交互.请参阅[CA1040:避免空接口](http://msdn.microsoft.com/en-us/library/ms182128(v = VS.100).aspx) (7认同)
  • @Brian:然而,这种语言使得标记界面的测试更容易(对于一个类),并且比查找属性更容易(对于一个对象).所以我要说代码分析人员对这个建议是错误的.如果他们有一个令人信服的案例,他们可以将它呈现给语言设计师并获得添加属性检测的功能.缺乏任何这样的功能是相当令人信服的,案件太弱了. (7认同)
  • 我没有提出任何埃里克,只是提出一个问题.我不会让编译器拒绝空类,接口或结构但是我确实传递了很多空类,正如Silviu所提到的,标记接口可能很有用.无论如何,我认为你的答案真的无法争辩:-) (5认同)
  • 另一方面,人们可以认为"允许空枚举"是一个需要测试的功能,等等.您如何确定哪些基线需要进行更改?您是否对祖先语言(C,C++,Java)的行为进行了基线测试,并在没有任何令人信服的动机的情况下选择与它们的一致性? (3认同)

Ale*_*kov 17

您可以将任何基础整数类型的值(我认为int默认情况下)转换为枚举 - 所以(MyConfusingEnum)42现在将是该枚举类型.

我认为一般来说这不是一个好主意,但可能存在"枚举"值来自外部源并且代码看起来更好的情况enum.

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

enum ExternalDeviceState {};

ExternalDeviceState GetState(){ ... return (ExternalDeviceState )intState;}
bool IsDeviceStillOk(ExternalDeviceState currentState) { .... }
Run Code Online (Sandbox Code Playgroud)

规范确实允许空枚举:

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   ,   }
Run Code Online (Sandbox Code Playgroud)

请注意,enum-member-declarations(opt)显式标记为变体,其中没有任何内容{}.


use*_*702 14

Activator.CreateInstance<MyConfusingEnum>();是一样的new MyConfusingEnum().(docs)

调用枚举的构造函数会为您0提供值.

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

出于设计决策的原因,我可以在一个标题为"为什么将int int int to invalid enum value not throw exception?"的问题上指向这个答案.

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


Pan*_*eof 7

有什么场景可以有用吗?

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

public enum Argb : int {}

public void SetColor(Argb a) { ....
Run Code Online (Sandbox Code Playgroud)

或者你想要一些扩展方法,而不是使用它们来填充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;
}
Run Code Online (Sandbox Code Playgroud)

并用它作为

var (alpha, red, green, blue) = color;
Run Code Online (Sandbox Code Playgroud)