C#编译器允许在另一个枚举类型声明中的不同枚举类型之间进行操作,如下所示:
public enum VerticalAnchors
{
Top=1,
Mid=2,
Bot=4
}
public enum HorizontalAnchors
{
Lef=8,
Mid=16,
Rig=32
}
public enum VisualAnchors
{
TopLef = VerticalAnchors.Top | HorizontalAnchors.Lef,
TopMid = VerticalAnchors.Top | HorizontalAnchors.Mid,
TopRig = VerticalAnchors.Top | HorizontalAnchors.Rig,
MidLef = VerticalAnchors.Mid | HorizontalAnchors.Lef,
MidMid = VerticalAnchors.Mid | HorizontalAnchors.Mid,
MidRig = VerticalAnchors.Mid | HorizontalAnchors.Rig,
BotLef = VerticalAnchors.Bot | HorizontalAnchors.Lef,
BotMid = VerticalAnchors.Bot | HorizontalAnchors.Mid,
BotRig = VerticalAnchors.Bot | HorizontalAnchors.Rig
}
但禁止在方法代码中使用它们,即操作:
VerticalAnchors.Top | HorizontalAnchors.Lef;
标记为此错误:
运营商'|'不能应用于'VerticalAnchors'和'HorizontalAnchors'类型的操作数。
当然有一种解决方法:
(int)VerticalAnchors.Top | (int)HorizontalAnchors.Lef
我很好奇这个编译器的行为。为什么在另一个枚举声明中允许不同枚举类型之间的操作,而不是其他地方?
答案 0 :(得分:11)
由于您没有在问题中提出问题,我会假装您提出一些有趣的问题并回答:
在枚举声明中,你可以在初始化器中使用其他枚举的值吗?
是。你可以说
enum Fruit { Apple }
enum Animal { Giraffe = Fruit.Apple }
即使在没有强制转换的情况下将Fruit.Apple
分配给Animal
类型的变量也是不合法的。
这个事实偶尔会令人惊讶。事实上,我自己也很惊讶。当我第一次尝试这样做时,为了测试部分编译器,我认为这是一个可能的错误。
规范中的哪个地方说它是合法的?
第14.3节说初始值设定项必须是常量,并且常量将转换为枚举的基础类型。枚举成员是常数。
啊,但是这个案子呢?
enum Fruit { Apple = 1 }
enum Shape { Square = 2 }
enum Animal { Giraffe = Fruit.Apple | Shape.Square }
好的,你让我在那里。第14.3节也说初始化器中使用的枚举成员不需要被强制转换,但是不清楚它是否意味着枚举的成员或任何枚举的成员。要么是合理的解释,要么没有特定的语言,很容易认为前者的含义是预期的。 因此,这是一个众所周知的缺陷;几年前我向Mads指出了它,它从未得到解决。一方面,规范并没有明确允许它。另一方面,这种行为既有用又符合规范中的精神(如果不完全在字母中)。该表达式首先不是一个合法的常量表达式,所以它是什么?
基本上,实现的作用是在处理枚举初始化程序时,它将所有枚举成员视为其基础类型的常量表达式。 (当然,它确实需要确保枚举定义是非循环的,但这可能最好留给另一个问题。)因此,它并没有“看到”Fruit
和Shape
没有“或“运营商定义。
虽然遗憾的是,规范措辞不明确,但这是一个理想的功能。事实上,我经常在Roslyn团队中使用它:
[Flags] enum UnaryOperatorKind {
Integer = 0x0001,
...
UnaryMinus = 0x0100,
...
IntegerMinus = Integer | UnaryMinus
... }
[Flags] enum BinaryOperatorKind {
...
IntegerAddition = UnaryOperatorKind.Integer | Addition
... }
能够从各种枚举中混合使用n-match标记非常方便。
答案 1 :(得分:10)
据我所知,它实际上不在spec。有一些相关的东西:
如果枚举成员的声明具有常量表达式 初始化程序,该常量表达式的值,隐式 转换为枚举的基础类型,是关联的值 枚举成员。
虽然VerticalAnchors.Top & HorizontalAnchors.Lef
的类型为VerticalAnchors
,但它可以隐式转换为VisualAnchors
。但这并不能解释为什么常量表达式本身支持各处的隐式转换。
实际上,它明显是反对规范:
常量表达式的编译时评估使用相同的 规则作为非常量表达式的运行时评估,除了 运行时评估会抛出异常,编译时 评估会导致发生编译时错误。
如果我没有遗漏某些内容,那么规范不仅不能明确地允许这一点,它也不允许这样做。在这种假设下,这将是一个编译器错误。
答案 2 :(得分:2)
C#允许枚举值的定义包含常量值表达式,因此枚举值可以是枚举的组合,例如: [Flags]
。编译器将表达式中的每个枚举值评估为int
(通常),因此您可以对枚举值执行按位和算术运算。
在枚举定义之外,您必须在对它执行操作之前将枚举转换为基本类型。
答案 3 :(得分:1)
有趣。您也可以问为什么允许这样做:
enum MyType
{
Member = DayOfWeek.Thursday | StringComparison.CurrentCultureIgnoreCase,
}
当不允许时
var local = DayOfWeek.Thursday | StringComparison.CurrentCultureIgnoreCase;
原因似乎是在枚举声明中,在枚举成员初始值设定项中,任何枚举值,甚至是无关枚举的值,都被认为是强制转换为其基础类型。所以编译器将上面的例子视为:
enum MyType
{
Member = (int)(DayOfWeek.Thursday) | (int)(StringComparison.CurrentCultureIgnoreCase),
}
我发现这很奇怪。我知道你可以直接使用相同枚举的值(不说明强制转换为基础类型),如下一行:
enum SomeType
{
Read = 1,
Write = 2,
ReadWrite = Read | Write,
}
但是我发现其他 enums'成员也被强制转换为它们的基础整数类型,这是非常令人惊讶的。