首页 > 编程笔记 > C#笔记 阅读:2

C# enum枚举用法详解(附带实例)

C# 中的枚举是一种特殊的值类型,我们能够在该类型中定义一组命名的数值常量。例如:
public enum BorderSide { Left, Right, Top, Bottom }

使用枚举类型的方法如下:
BorderSide topSide = BorderSide.Top;
bool isTop = (topSide == BorderSide.Top); // true

每一个枚举成员都对应一个整数。在默认情况下:
当然,可以指定其他的整数类型代替默认类型,例如:
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 进行特殊对待的原因有如下两个:

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 }

相关文章