简体   繁体   English

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

[英]Why does the C# compiler allow empty enums?

I accidentally defined an enum today that contained no values. 我今天不小心定义了一个不包含任何值的枚举。 Like this one for example: 像这样一个例子:

public enum MyConfusingEnum{}

The compiler was quite happy to let me define that and the code built successfully. 编译器很高兴让我定义它并成功构建了代码。

Now I obviously can't use that in the traditional sense since the code,.. 现在我显然不能使用传统意义上的代码,因为代码..

var mySadCompiler = MyConfusingEnum;

doesn't specify a value but interestingly enough I was able to say,.. 没有指定值,但有趣的是, 能够说,..

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

which, as I've alluded to, is a value type of MyConfusingEnum with the value 0; 正如我提到的那样,它是MyConfusingEnum的值类型,值为0;

My question is why does the compiler allow an empty definition and are there any scenarios where it could be useful? 我的问题是,为什么编译器允许空定义,并且在任何情况下它都可能有用?

First off, you could have done that much more easily: 首先,你可以做的是容易:

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

all of the above work just fine. 以上所有工作都很好。 (You may be surprised that the first works; see the specification section on implicit enumeration conversions for details.) (您可能会对第一种方法感到惊讶;有关详细信息,请参见有关隐式枚举转换的规范部分。)

My question is why does the compiler allow an empty definition 我的问题是为什么编译器允许空定义

I'll begin by answering your question with a question. 首先,用一个问题回答您的问题。 Would you also have the compiler reject? 您还会拒绝编译器吗?

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

Why or why not? 为什么或者为什么不?

To more directly not answer your question: "why is the world not different than it is?" 更直接地不回答您的问题:“为什么世界没有不同?” questions are hard to answer. 问题很难回答。 Instead of answering that impossible question, I'll answer the question "suppose making empty enums an error had been pitched to the design team; how would you have responded to that pitch?" 我将不回答这个不可能的问题,而是回答“假设使空枚举错误已向设计团队提出了一个问题;您将如何应对这个问题?” That question is still counterfactual but at least it is one I can answer. 这个问题仍然是事实,但至少我可以回答。

The question then becomes whether the cost of the feature is justified by its benefits . 问题在于,该功能的成本是否可以通过其利益得到证明。

In order to work a language feature must be thought of, designed, specified, implemeted, tested, documented and shipped to customers. 为了工作,必须考虑,设计,指定,实现,测试,记录和交付给客户的语言功能。 This is a "produce an error" feature, so the error message must be written and translated into some dozen languages, as must the documentation. 这是“产生错误”的功能,因此必须将错误消息编写并翻译成十几种语言,文档也必须如此。 The five minutes it would have taken me to implement the feature translate into many hours of work by many people who are paid rather a lot. 我花了五分钟的时间来实现该功能,这使许多人获得了很多工作,而他们的薪水却很高。

However, that is not actually the relevant cost. 但是,这实际上并不是相关费用。 The opportunity cost is the relevant cost. 机会成本是相关成本。 Budgets are finite, features are not free, and therefore any feature which is implemented means some other feature must be cut; 预算是有限的,功能不是免费的,因此实现的任何功能都意味着必须削减一些其他功能。 which feature of C# would you like to have been cut to get this feature in? 您想要削减C#的哪个功能以使用此功能? The lost benefit from not being able to do a better feature is the opportunity cost . 无法做得更好的功能而失去的利益是机会成本

I note also that your proposed feature has zero obvious benefit to anyone , which would make it a tough sell to the design committee. 我还注意到,您提出的功能对任何人没有明显的好处 ,这对设计委员会来说是很难的。 Perhaps there is a compelling benefit that I'm not seeing; 也许我没有看到引人注目的好处; if so, what is it? 如果是这样,那是什么?

are there any scenarios where it could be useful? 有没有可能有用的方案?

None come to mind. 没有人想到。 "Rejecting programs which are not obviously useful" is not a design goal of C#. “拒绝没有明显用处的程序”不是C#的设计目标。

You can cast any value of underlying integer type (I think int by default) to the enum - so (MyConfusingEnum)42 now will be of that enum type. 您可以将任何基础整数类型的值(默认情况下为int )转换为枚举-因此(MyConfusingEnum)42现在将是该枚举类型。

I don't think it is good idea in general, but there could be cases when "enum" values are coming from external source and code just looks nicer with enum . 我不认为这是一般的好主意,但有可能是在“枚举”值从外部源和代码来只是看起来具备更好的情况下enum

Sample (assuming code incapsulates some "int-based state" in Enum: 示例(假设代码在Enum中封装了一些“基于int的状态”:

enum ExternalDeviceState {};

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

Specification does indeed allow empty enum: 规范确实允许空枚举:

14.1 Enum declarations 14.1枚举声明

An enum declaration declares a new enum type. 枚举声明声明了新的枚举类型。 An enum declaration begins with the keyword enum, and defines the name, accessibility, underlying type, and members of the enum. 枚举声明以关键字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   ,   }

Note that enum-member-declarations(opt) is explicitly marked as variant where nothing is inside {} . 请注意, enum-member-declarations(opt)被明确标记为{}内没有任何内容的变体。

Activator.CreateInstance<MyConfusingEnum>(); is the same as new MyConfusingEnum() . new MyConfusingEnum()相同。 ( docs ) docs

Calling the constructor of an enum gives you 0 as value. 调用枚举的构造函数会为您提供0作为值。

Because of a design decision, an enum can have any value that is valid for the backing type (usually int ), it doesn't have to be a value defined in the enum. 由于设计决策,枚举可以具有对于后备类型有效的任何值(通常是int ),而不必是枚举中定义的值。

For the reason of that design decision, I can point you to this answer on a question titled "Why does casting int to invalid enum value NOT throw exception?" 出于该设计决策的原因,我可以在标题为“为什么将int强制转换为无效的枚举值时不抛出异常?”的问题上指出这个答案

@AlexeiLevenkov has provided the spec that allows an empty enum, we can guess that the rationale for this is that since any backing type value is valid, an empty enum is allowed. @AlexeiLevenkov提供了允许使用空枚举的规范,我们可以猜测其基本原理是,由于任何后备类型值都是有效的,因此允许使用空枚举。

are there any scenarios where it could be useful? 有没有可能有用的方案?

As other have already mention you can assign to this enum any value that the underlying type permits by a simple cast. 正如其他人已经提到的那样,您可以通过简单的强制转换将基础类型允许的任何值分配给该枚举。 That way you can enforce Type Checking, in situations where an int would make thing confusing. 这样,在int会使事情变得混乱的情况下,您可以强制执行类型检查。 For example: 例如:

public enum Argb : int {}

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

or you want to have some extension methods without clutering the int datatype, with them 或者您想拥有一些扩展方法而又不影响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;
}

and use it as 并用作

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

Are there any scenarios where it could be useful? 有没有可能有用的方案?

I experienced one in the Java world. 我在Java世界中经历过。

In case of enums, you know all possible values at compile time . 对于枚举, 您在编译时知道所有可能的值

However, there is a time before compile time in which you may not know all values (yet) or in which you simply do not intend to implement any values, yet. 但是,在编译之前会有一段时间,您可能还不知道所有值,或者根本不打算实现任何值。

In the course of designing an API, I implemented an enumeration without any values to enable referencing it from other interfaces. 在设计API的过程中,我实现了一个没有任何值的枚举,以允许从其他接口引用它。 I added values a later time. 我稍后再添加值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么 C# 编译器允许嵌套范围内的重复变量? - Why does the C# compiler allow a duplicated variable in nested scope? 为什么 C# 编译器允许使用 Linq 而不是使用括号执行强制转换? - Why does the C# compiler allow a cast to be performed with Linq but not with parentheses? C#编译器是否优化空语句 - Does C# compiler optimize empty statements 如果调用ToList或ToArray,为什么C#编译器允许在表达式树中使用动态操作 - Why does C# compiler allow use of a dynamic operation in expression tree if ToList or ToArray is called 为什么c#编译器允许返回值类型和变量类型的错误匹配? - Why does c# compiler allow incorrect match of return value type and variable type? 为什么C#编译器允许在IEnumerable <T>和TAlmostAnything之间进行显式转换? - Why does the C# compiler allow an explicit cast between IEnumerable<T> and TAlmostAnything? 为什么零长度堆栈分配使 C# 编译器乐于允许条件堆栈分配? - Why does a zero-length stackalloc make the C# compiler happy to allow conditional stackallocs? 为什么 C# 编译器处理空异步任务方法与仅返回 Task.CompletedTask 不同? - Why does the C# compiler handle empty async Task methods different to just return Task.CompletedTask? 为什么C#编译器会在此代码上崩溃? - Why does the C# compiler crash on this code? 为什么C#允许异构IList - Why does C# allow heterogeneous IList
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM