C#图解教程 第十一章 枚举

枚举

枚举

设置底层类型和显式值
隐式成员编号

位标志

Flags特性
使用位标志的示例

关于枚举的补充

枚举

枚举



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

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

例:枚举示例

关键字 枚举名称
  ↓      ↓
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  =10,
    Yellow =15,
    Red    =15
}
隐式成员编号

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

例:

enum CardSuit
{
    Hearts,    //0 因为这是第一项
    Clubs,     //1 比之前大1
    Diamonds,  //2 比之前大1,下面的以此类推
    Spades,
    MaxSuits
}
enum FaceCards
{
    //Member             //所赋的值
    Jack    =11,         //11 显式设置
    Queen,               //12 比之前大1
    King,                //13 比之前大1
    Ace,                 //14 比之前大1
    NumberOfFaceCards=4, //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),1));
        foreach(var name in Enum.GetNames(typeof(TrafficLight)))
        {
            Console.WriteLine(name);
        }
    }
}

时间: 2024-10-29 19:10:43

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

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

原博客地址:http://jinnianshilongnian.iteye.com/blog/2018398 根据下载的pdf学习. 第二十一章 授予身份与切换身份(一) 1.使用场景 某个领导因为某些原因不能访问一些网站,他想把这个网站上的工作委托给秘书,但是他又不想提供账户.密码.此时可以使用shiro的 RunAs 功能. RunAs:允许一个用户假装为另一个用户(如果获得了允许)的身份进行访问. 注意,本章代码基于<第十六章 综合实例>,详细的数据模型及基本流程见该章. 2.表及数据

Flask 教程 第十一章:美化

本文翻译自The Flask Mega-Tutorial Part XI: Facelift 这是Flask Mega-Tutorial系列的第十一部分,我将告诉你如何用基于Bootstrap用户界面框架的新模板替换基础的HTML模板. 你把玩Microblog应用也有一段时间了,所以我相信你已经注意到,我没有花太多时间来美化它,说得更具体点,我根本没有花时间. 所有的模板只使用了基础样式,没有任何自定义的展现. 这对于我来说却非常有用,因为我可以专注于应用的实际逻辑,不用分心于编写好看的HTM

C# 图解教程 第五章

方法的结构方法体内部代码的执行本地变量    类型推断和var关键字    嵌套块中的本地变量本地常量控制流方法调用返回值返回语句和void方法参数    形参    实参值参数引用参数引用类型作为值参数和引用参数输出参数参数数组    方法调用    用数组作为实参参数类型总结方法重载命名参数可选参数栈帧递归 方法 方法的结构 方法是一块具有名称的代码. 可以使用方法的名称从别的地方执行代码,也可以把数据传入方法并接收数据输出. 方法是类的函数成员,主要有两个部分,方法头和方法体. 方法头 指

C#图解教程 第十三章 委托

委托 什么是委托委托概述声明委托类型创建委托对象给委托赋值组合委托为委托添加方法从委托移除方法调用委托委托示例调用带返回值的委托调用带引用参数的委托匿名方法 使用匿名方法匿名方法的语法 Lambda 表达式 委托 什么是委托 可以认为委托是持有一个或多个方法的对象.当然,正常情况下你不想"执行"一个对象,但委托与典型对象不同.可以执行委托,这时委托会执行它所"持有"的方法. 我们从下面的示例代码开始.具体细节将在本章剩余内容介绍. 代码开始部分声明了一个委托类型My

C#图解教程 第六章 深入理解类

深入理解类 类成员成员修饰符的顺序实例类成员静态字段从类的外部访问静态成员 静态字段示例静态成员的生存期 静态函数成员其他静态类成员类型成员常量常量与静态量属性 属性声明和访问器属性示例使用属性属性和关联字段执行其他计算只读和只写属性属性与公共字段计算只读属性示例自动实现属性静态属性 实例构造函数 带参数的构造函数默认构造函数 静态构造函数对象初始化语句析构函数readonly修饰符this关键字索引器 什么是索引器索引器和属性声明索引器索引器的set访问器索引器的get访问器关于索引器的补充为

C#图解教程 第七章 类和继承

类和继承 类继承 通过继承我们可以定义一个新类,新类纳入一个已经声明的类并进行扩展. 可以使用已存在的类作为新类的基础.已存在类称为基类(base class),新类称为派生类(derived class).派生类组成如下: 本身声明中的成员 基类的成员 声明派生类,需要在类名后加入基类规格说明 派生类扩展它的基类,因为它包含了基类的成员,加上它本身声明中的新增功能 派生类不能删除它所继承的任何成员 例:OtherClass类,继承自SomeClass class OtherClass:Some

C# 图解教程 第三章 类型、存储和变量

类型.存储和变量 C#程序是一组类型声明 C程序是一组函数和数据类型 C++程序是一组函数和类 C#程序是一组类型声明 C#程序或DLL源码是一组一种或多种类型声明 对于可执行程序,类型声明中必须有一个包含Main方法的类 命名空间是一种把相关类型声明分组并命名的方法 类型是一种模板 可以把类型想象成一个用来创建数据结构的模板.模板本身不是数据结构,但它说明了由该模板构造的对象的特征. 类型由下面的元素定义: 名称 用于保存数据成员的数据结构 一些行为及约束条件 例:short类型和int类型

D3.js的v5版本入门教程(第十一章)——交互式操作

D3.js的v5版本入门教程(第十一章) 与图形进行交互操作是很重要的!所谓的交互操作也就是为图形元素添加监听事件,比如说当你鼠标放在某个图形元素上面的时候,就会显示相应的文字,而当鼠标移开后,文字就会消失,或者鼠标单击一下某图形元素就会使它动起来 为了与图形元素进行交互操作,我们还是需要以下新的知识点 on("eventName",function):该函数是添加一个监听事件,它的第一个参数是事件类型,第二个参数是响应事件的内容d3.select(this),选择当前元素    常见

《C#图解教程》读书笔记之二:存储、类型和变量

一.类型初窥:掀起你的盖头来 (1)C程序是一组函数和数据类型,C++程序是一组函数和类,而C#程序是一组类型声明: (2)类型是一种模板:模板本身不是数据结构,但它详细说明了由该模板构造的对象的特征: (3)C#提供了16种预定义类型:13种简单类型(数值类型:int,float,double,decimal等:非数值类型:bool,char),3种非简单类型(object,string,dynamic): 所有的预定义类型都直接映射到底层的.NET类型.C#的类型名称其实就是.NET类型的别