C# enum枚举用法详解(附带实例)
C# 中的枚举是一种特殊的值类型,我们能够在该类型中定义一组命名的数值常量。例如:
使用枚举类型的方法如下:
每一个枚举成员都对应一个整数。在默认情况下:
当然,可以指定其他的整数类型代替默认类型,例如:
也可以显式指定每一个枚举成员对应的值:
编译器还支持显式指定部分枚举成员。没有指定的枚举成员,在最后一个显式指定的值基础上递增。因此上例等价于:
也可以显式将一个枚举类型转换为另一个。假设 HorizontalAlignment 定义为:
则两个枚举类型之间的转换是通过对应的整数值进行的:
在枚举表达式中,编译器会特殊对待数值字面量 0,它不需要进行显式转换:
可以使用位运算符合并枚举类型的值,例如,| 和 &,它们将作用在对应的整数值上。
一般来说,合并枚举类型通常用复数名词而不用单数形式。为了方便起见,可以将合并的成员直接放在枚举的声明内:
由于枚举类型可以和它对应的整数类型相互转换,因此枚举的真实值可能超出枚举类型成员的数值范围:
位运算符和算术运算符也会产生类似的非法值:
非法的 BorderSide 的枚举值可能破坏如下的程序:
针对上述问题的解决方案之一是再加上一个 else 子句:
而另一个解决方案是显式检查枚举值的合法性。可以使用静态方法 Enum.IsDefined 来执行该操作:
遗憾的是,Enum.IsDefined 对标志枚举类型不起作用,然而下面的方法(巧妙使用了 Enum.ToString() 的行为)可以在标志枚举类型合法时返回 true:
public enum BorderSide { Left, Right, Top, Bottom }
使用枚举类型的方法如下:
BorderSide topSide = BorderSide.Top; bool isTop = (topSide == BorderSide.Top); // true
每一个枚举成员都对应一个整数。在默认情况下:
- 对应的数值是 int 类型的;
- 按照枚举成员的声明顺序,自动按照 0、1、2…… 进行常量赋值。
当然,可以指定其他的整数类型代替默认类型,例如:
public enum BorderSide : byte { Left, Right, Top, Bottom }
也可以显式指定每一个枚举成员对应的值:
public enum BorderSide : byte { Left = 1, Right = 2, Top = 10, Bottom = 11 }
编译器还支持显式指定部分枚举成员。没有指定的枚举成员,在最后一个显式指定的值基础上递增。因此上例等价于:
public enum BorderSide : byte { Left = 1, Right, Top = 10, Bottom }
C#枚举类型转换
枚举类型的实例可以与它对应的整数值相互显式转换:int i = (int)BorderSide.Left; BorderSide side = (BorderSide)i; bool leftOrRight = (int)side <= 2;
也可以显式将一个枚举类型转换为另一个。假设 HorizontalAlignment 定义为:
public enum HorizontalAlignment { Left = BorderSide.Left, Right = BorderSide.Right, Center }
则两个枚举类型之间的转换是通过对应的整数值进行的:
HorizontalAlignment h = (HorizontalAlignment)BorderSide.Right; //等同于: HorizontalAlignment h = (HorizontalAlignment)(int)BorderSide.Right;
在枚举表达式中,编译器会特殊对待数值字面量 0,它不需要进行显式转换:
BorderSide b = 0; // 无需强制转换 if (b == 0) ...对 0 进行特殊对待的原因有如下两个:
- 第一个枚举成员经常作为默认值;
- 在合并枚举类型中,0 表示无标志。
C#标志枚举类型
枚举类型的成员可以合并。为了避免混淆,合并枚举类型的成员要显式指定值,典型的值为 2 的幂次。例如:enum BorderSides { None = 0, Left = 1, Right = 2, Top = 4, Bottom = 8 }或者:
enum BorderSides { None = 0, Left = 1, Right = 1 << 1, Top = 1 << 2, Bottom = 1 << 3 }
可以使用位运算符合并枚举类型的值,例如,| 和 &,它们将作用在对应的整数值上。
BorderSides leftRight = BorderSides.Left | BorderSides.Right; if ((leftRight & BorderSides.Left) != 0) Console.WriteLine("Includes Left"); // Includes Left string formatted = leftRight.ToString(); // "Left, Right" BorderSides s = BorderSides.Left; s |= BorderSides.Right; Console.WriteLine(s == leftRight); // True s ^= BorderSides.Right; // 切换 BorderSides.Right Console.WriteLine(s); // Left按照惯例,当枚举类型的成员可以合并时,其枚举类型一定要应用 Flags 特性。如果声明了一个没有标注 Flags 特性的枚举类型,其成员依然可以合并,但若在该枚举实例上调用 ToString 方法,则会输出一个数值而非一组名字。
一般来说,合并枚举类型通常用复数名词而不用单数形式。为了方便起见,可以将合并的成员直接放在枚举的声明内:
enum BorderSides { None = 0, Left = 1, Right = 1 << 1, Top = 1 << 2, Bottom = 1 << 3, LeftRight = Left | Right, TopBottom = Top | Bottom, All = LeftRight | TopBottom }
C# 枚举运算符
枚举类型可用的运算符有:=、==、!=、<、>、<=、>=、+、-、^、&、|、~、+=、-=、++、--、sizeof
位运算符、算术运算符和比较运算符都返回对应整数值的运算结果。枚举类型和整数类型之间可以做加法,但两个枚举类型之间不能做加法。C#类型安全问题
请看下面的枚举类型:public enum BorderSide { Left, Right, Top, Bottom }
由于枚举类型可以和它对应的整数类型相互转换,因此枚举的真实值可能超出枚举类型成员的数值范围:
BorderSide b = (BorderSide)12345; Console.WriteLine(b); // 12345
位运算符和算术运算符也会产生类似的非法值:
BorderSide b = BorderSide.Bottom; b++; // 无错误
非法的 BorderSide 的枚举值可能破坏如下的程序:
void Draw(BorderSide side) { if (side == BorderSide.Left) { ... } else if (side == BorderSide.Right) { ... } else if (side == BorderSide.Top) { ... } else { ... } // 假设是 BorderSide.Bottom }
针对上述问题的解决方案之一是再加上一个 else 子句:
else if (side == BorderSide.Bottom) { ... } else throw new ArgumentException("Invalid BorderSide: " + side, "side");
而另一个解决方案是显式检查枚举值的合法性。可以使用静态方法 Enum.IsDefined 来执行该操作:
BorderSide side = (BorderSide)12345; Console.WriteLine(Enum.IsDefined(typeof(BorderSide), side)); // False
遗憾的是,Enum.IsDefined 对标志枚举类型不起作用,然而下面的方法(巧妙使用了 Enum.ToString() 的行为)可以在标志枚举类型合法时返回 true:
for (int i = 0; i <= 16; i++) { BorderSides side = (BorderSides)i; Console.WriteLine(IsFlagDefined(side) + " " + side); } bool IsFlagDefined(Enum e) { decimal d; return !decimal.TryParse(e.ToString(), out d); } [Flags] public enum BorderSides { Left = 1, Right = 2, Top = 4, Bottom = 8 }