枚举

枚举


枚举是由程序员定义的类型与类或结构一样。

  • 与结构一样,枚举是值类型,因此直接存储它们的数据,而不是分开存储成引用和数据
  • 枚举只有一种类型的成员:命名的整数值常量

例:枚举示例

关键字 枚举名称
↓ ↓
enum TrafficLight
{
Green, ← 逗号分隔,没有分号
Yellow,
Red
}

每个枚举类型都有一个底层整数类型,默认为int。

  • 每个枚举成员都被赋予一个底层类型的常量值
  • 在默认情况下,编译器把第一个成员赋值为0,并对每个后续成员赋的值比前一个多1
var t1=TrafficLight.Green;
var t2=TrafficLight.Yellow;
var t3=TrafficLight.Red;
Console.WriteLine("{0},\t{1}",t1,(int)t1);
Console.WriteLine("{0},\t{1}",t2,(int)t2);
Console.WriteLine("{0},\t{1}",t3,(int)t3);

设置底层类型和显式值

可以把冒号和类型名放在枚举名之后,这样就可以使用int以外的整数类型。类型可以是任何整数类型。所有成员常量都属于枚举的底层类型。

enum TrafficLight:ulong
{
...
}

成员常量的值可以是底层类型的任何值。枚举成员不能有重复的名称,但可以有重复的值。

enum TrafficLight
{
Green =,
Yellow =,
Red =
}
隐式成员编号

如果不初始化一个成员常量,编译器隐式给它赋一个值。
例:

enum CardSuit
{
Hearts, //0 因为这是第一项
Clubs, //1 比之前大1
Diamonds, //2 比之前大1,下面的以此类推
Spades,
MaxSuits
}
enum FaceCards
{
//Member //所赋的值
Jack =, //11 显式设置
Queen, //12 比之前大1
King, //13 比之前大1
Ace, //14 比之前大1
NumberOfFaceCards=, //4 显式设置
SomeOtherValue, //5 比之前大1
HighestFaceCard=Ace //14 以上定义了Ace
}

位标志


程序员们长期使用单个字(single word)的不同位作为一组开/关标志的紧凑方法。本节将其称为标志字(flag word)。枚举提供了实现它的简便方法。

一般步骤如下。

  1. 确定需要多少个位标志,并选择一个有足够多位的无符号类型来保存它
  2. 确定每个位位置代表什么,并给它们名称。声明一个选中的整数类型枚举,每个成员由一个位位置表示
  3. 使用按位或(OR)运算符设置保持该位标志的字中的适当的位
  4. 使用按位与(AND)运算符,或HasFlag方法解开位标志

例:下面枚举表示纸牌游戏中的一副牌的选项。

  • 成员有表示二进制选项的名称

    • 每个选项由字中的一个特殊的位表示,位位置保持一个0或一个1
    • 因为一个位标志表示一个或开或关的位,所以你不会想用0作为一个成员值。它已经有了一个意思:所有的位标志都是关
  • 在16进制表示法中,每个16机制数字用4位来表示。由于位模式和16进制表示法之间的联系,所以在处理位模式时,常使用16进制而不是10进制
  • 使用Flags特性装饰(decorate)枚举实际上不必要,但可以有一些额外的便利,很快会讨论这一点。特性表现为用中括号括起来的字符串,出现在语言构造之前。在本例中,特性出现在枚举声明之前。特性在第24章阐述
[Flags]
enum CardDeckSettings:uint
{
SingleDeck =0x01, //位0
LargePictures =0x02, //位1
FancyNumbers =0x04, //位2
Animation =0x08 //位3
}

要创建一个带有适当位标志的字,需要声明一个该枚举类型的变量,并使用按位或运算符设置需要的位。

CardDeckSettings ops= CardDeckSettings.SingleDeck
|CardDeckSettings.FancyNumbers
|CardDeckSettings.Animation;

判断标志字是否包含特定的位标志集,可以使用枚举类型中的HasFlag布尔方法。在标志字上调用HasFlag方法,并将要检查的位标志作为参数。如果设置了指定的位标志,HasFlag返回true,否则返回false。

bool useFancyNumbers=ops.HasFlag(CardDeckSettings.FancyNumbers);

HasFlag还可以检测多个位标志。

  • 第一行创建了一个测试字实例,叫做testFlags,设置了Animation和FancyNumbers标志位
  • 然后把叫做testFlags作为参数传给HasFlag方法
  • HasFlag检测是否测试字中的所有标志都在ops标志字中进行了设置。如果是返回true,否则返回false
CardDeckSettings testFlags=CardDeckSettings.Animation|CardDeckSettings.FancyNumbers;
bool useAnimationAndFancyNumbers=ops.HasFlag(testFlags);

另外一种判断是否设置了一个或多个指定为的方法是使用按位与运算符。

bool useFancyNumbers=
(ops&CardDeckSettings.FancyNumbers)==CardDeckSettings.FancyNumbers;

Flags特性
[Flags]
enum CardDeckSettings:uint
{
SingleDeck =0x01, //位0
LargePictures =0x02, //位1
FancyNumbers =0x04, //位2
Animation =0x08 //位3
}

Flags特性不会改变计算结果,但却提供了一些方便的特性。首先,它通知编译器、对象浏览器以及其他查看这段代码的工具,该枚举的成员不仅可以用作单独的值,还可以按位标志进行组合。这样浏览器就可以更恰当地解释该枚举类型的变量。
其次,它允许枚举的ToString方法为位标志的值提供更多的格式化信息,ToString方法以一个枚举值位参数,将其与枚举的常量成员相比较。如果与某个成员相匹配,ToString返回该成员的字符串名称。
例:没有Flags的枚举

enum CardDeckSettings:uint
{
SingleDeck =0x01, //位0
LargePictures =0x02, //位1
FancyNumbers =0x04, //位2
Animation =0x08 //位3
}
class Program
{
static void Main()
{
CardDeckSettings ops;
ops=CardDeckSettings.FancyNumbers;
Console.WriteLine(ops.ToString());
ops=CardDeckSettings.FancyNumbers|CardDeckSettings.Animation;
Console.WriteLine(ops.ToString());
}
}

上面输出结果的第二行12=8+4。因为FancyNumbers将位设置为值4,Animation将位设置为值8。
然而,如果在枚举声明前加上Flags特性,将告诉ToString方法位可以分开考虑。运行包含Flags特性的代码,结果如下:


使用位标志的示例
[Flags]
enum CardDeckSettings:uint
{
SingleDeck =0x01, //位0
LargePictures =0x02, //位1
FancyNumbers =0x04, //位2
Animation =0x08 //位3
}
class MyClass
{
bool UseSingleDeck =false,
UseBigPics =false,
UseFancyNumbers =false,
UseAnimation =false,
UseAnimationAndFancyNumbers =false;
public void SetOptions(CardDeckSettings ops)
{
UseSingleDeck=ops.HasFlag(CardDeckSettings.SingleDeck);
UseBigPics=ops.HasFlag(CardDeckSettings.LargePictures);
UseFancyNumbers=ops.HasFlag(CardDeckSettings.FancyNumbers);
UseAnimation=ops.HasFlag(CardDeckSettings.Animation);
CardDeckSettings testFlags=CardDeckSettings.Animation|CardDeckSettings.FancyNumbers;
UseAnimationAndFancyNumbers=ops.HasFlag(testFlags);
}
public void PrintOptions()
{
Console.WriteLine("Option settings:");
Console.WriteLine("Use Single Deck - {0}",UseSingleDeck);
Console.WriteLine("Use Large Pictures - {0}",UseBigPics);
Console.WriteLine("Use Fancy Numbers - {0}",UseFancyNumbers);
Console.WriteLine("Show Animation - {0}",UseAnimation);
Console.WriteLine("Show Animation And FancyNumbers - {0}",UseAnimationAndFancyNumbers);
}
}
class Program
{
static void Main()
{
var mc=new MyClass();
CardDeckSettings ops=CardDeckSettings.SingleDeck
|CardDeckSettings.FancyNumbers
|CardDeckSettings.Animation;
mc.SetOption(ops);
mc.PrintOptions();
}
}

关于枚举的补充


枚举只有单一的成员类型:声明的成员常量

  • 不能对成员使用修饰符。它们都隐式地具有和枚举相同的可访问性
  • 由于成员是常量,即使在没有该枚举变量时也可以访问。使用枚举类型名.成员名

例:直接访问枚举常量

Console.WriteLine("{0}",TrafficLight.Green);

枚举是一种独特的类型。比较不同枚举类型的成员会导致编译时错误。
例: 枚举比较

  • 第一个if正确,因为它比较同一枚举类型的不同成员
  • 第二个if会产生一个错误,因为它比较来自不同枚举类型的成员,尽管它们的结构和成员名称相同
enum FirstEnum
{
Mem1,
Mem2
}
enum SecondEnum
{
Mem1,
Mem2
}
class Program
{
static void Main()
{
if(FirstEnum.Mem1<FirstEnum.Mem2)
Console.WriteLine("True");
if(FirstEnum.Mem1<SecondEnum.Mem1) //错误,不同枚举类型
Console.WriteLine("True");
}
}

.NET Enum类型还包括一些有用的静态方法:

  • GetName方法以枚举类型对象和整数为参数,返回响应的枚举成员的名称
  • GetNames方法以枚举类型对象为参数,返回该枚举中所有成员的名称

例:GetName、GetNames示例

enum TrafficLight
{
Green,
Yellow,
Red
}
class Program
{
static void Main()
{
Console.WriteLine("Second member of TrafficLight is {0}\n",Enum.GetName(typeof(TrafficLight),));
foreach(var name in Enum.GetNames(typeof(TrafficLight)))
{
Console.WriteLine(name);
}
}
}

C#图解教程 第十一章 枚举的更多相关文章

  1. C#图解教程 第二十一章 命名空间和程序集

    命名空间和程序集 引用其他程序集 mscorlib库 命名空间 命名空间名称命名空间的补充命名空间跨文件伸展嵌套命名空间 using 指令 using命名空间指令using别名指令程序集的结构 程序集 ...

  2. 2017.2.15 开涛shiro教程-第二十一章-授予身份与切换身份(二) controller

    原博客地址:http://jinnianshilongnian.iteye.com/blog/2018398 根据下载的pdf学习. 开涛shiro教程-第二十一章-授予身份与切换身份(二) 1.回顾 ...

  3. 2017.2.15 开涛shiro教程-第二十一章-授予身份与切换身份(一) table、entity、service、dao

    原博客地址:http://jinnianshilongnian.iteye.com/blog/2018398 根据下载的pdf学习. 第二十一章 授予身份与切换身份(一) 1.使用场景 某个领导因为某 ...

  4. python 教程 第二十一章、 扩展Python

    第二十一章. 扩展Python /* D:\Python27\Lib\Extest-1.0\Extest2.c */ #include <stdio.h> #include <std ...

  5. python 教程 第十一章、 异常

    第十一章. 异常 1)    try/except/else格式 try: s = raw_input('--> ') except EOFError: print 'Why did you d ...

  6. Flask 教程 第二十一章:用户通知

    本文翻译自The Flask Mega-Tutorial Part XXI: User Notifications 这是Flask Mega-Tutorial系列的第二十一章,我将添加一个私有消息功能 ...

  7. Flask 教程 第十一章:美化

    本文翻译自The Flask Mega-Tutorial Part XI: Facelift 这是Flask Mega-Tutorial系列的第十一部分,我将告诉你如何用基于Bootstrap用户界面 ...

  8. C#图解教程 第十八章 枚举器和迭代器

    枚举器和迭代器 枚举器和可枚举类型 foreach语句 IEnumerator接口 使用IEnumerable和IEnumerator的示例 泛型枚举接口迭代器 迭代器块使用迭代器来创建枚举器使用迭代 ...

  9. C#4.0图解教程 - 第24章 反射和特性 – 2.特性

    1.特性 定义 Attribute用来对类.属性.方法等标注额外的信息,贴一个标签(附着物) 通俗:给 类 或 类成员 贴一个标签,就像航空部为你的行李贴一个标签一样 注意,特性 是 类 和 类的成员 ...

随机推荐

  1. 【linux之用户,密码,组管理】

    一.用户及密码 用户账户 超级用户:UID=0 root 普通用户:UID!=0 系统用户: 0<UID<500 为了维持系统的某些功能或者实现某些服务 不能完成登录时候的身份验证 普通用 ...

  2. 单用户模式与救援模式:linux学习第三篇

    单用户模式 1.      重新启动,在下列界面选项第一项按 e 按e后进入此grub界面(启动工具) 2.      找到linux16所在行,将'ro'(只读)修改为'rw'(读写),并加上 in ...

  3. php define和const的区别

    1.使用const使得代码简单易读,const本身就是一个语言结构,而define是一个函数2.const用于类成员变量的定义,一经定义,不可修改.3.Define不可以用于类成员变量的定义,可用于全 ...

  4. php生成文字水印和图片水印

    生成文字水印   //文字水印 /*打开图片*/ //1.配置图片路径 $src = "4.jpg"; //2.获取图片的信息(得到图片的基本信息) $info = getimag ...

  5. CentOS源码安装Python3.6

    一.安装环境及版本 CentOS 6.5 Python 3.6.1 二.安装依赖包 1.安装静态库 # yum install -y openssl-static 注:如果不安装该静态库,会导致pyt ...

  6. php实现301跳转

    php实现301跳转代码 <?php Header("HTTP/1.1 301 Moved Permanently"); Header("Location: htt ...

  7. 使用js dom和jquery分别实现简单增删改

    <html><head> <meta http-equiv="Content-Type" content="text/html; chars ...

  8. 使用JS代码实现点击按钮下载文件

    有时候我们在网页上需要增加一个下载按钮,让用户能够点击后下载页面上的资料,那么怎样才能实现功能呢?这里有两种方法: 现在需要在页面上添加一个下载按钮,点击按钮下载文件. 题外话,这个下载图标是引用的 ...

  9. 浅谈PHP答题卡识别(一)

    最近期末考试考完了,我们也要放寒假了.于是突发奇想,想用PHP写一个答题卡识别程序.已经实现了一些,现分享给大家. 具体的步骤如下: 上传答题卡=>图片二值化(已实现)=>寻找定位点(已实 ...

  10. python并发编程之多进程(三):共享数据&进程池

    一,共享数据 展望未来,基于消息传递的并发编程是大势所趋 即便是使用线程,推荐做法也是将程序设计为大量独立的线程集合 通过消息队列交换数据.这样极大地减少了对使用锁定和其他同步手段的需求, 还可以扩展 ...