枚举类型和位标志

一、枚举类型
  枚举类型(enumerated types)定义了一组"符号名称/值"配对。
  例如,以下Color类型定义了一组符号,每个符号都标识一种颜色:

复制代码
internal enum Color {
While,        //赋值0
Red,          //赋值1
Green,        //赋值2
Blue,         //赋值3
Orange        //赋值4
}
复制代码

  使用枚举类型的好处:  

  1)枚举类型使程序更容易编写、阅读和维护。有了枚举类型,符号名称可在代码中随便使用,开发人员不需要记 住每个硬编码的含义。而且,一旦与符号名称对应的值发生变化,代码也可以简单的重新编译,不需要对源代码做出任何修改。除此之外,文档工具和其他实用程序能向开发人员显示有意义的符号名称。

  2)枚举类型是强类型的。

  在Microsoft .NET Framework中,枚举类型不只是编译器所关心的符号,它在类型系统中还具有"一等公民"的地 位,能实现非常强大的操作。枚举类型都直接从System.Enum派生,后者从System.ValueType派生,而System.ValueType用从 System.Object派生。所以,枚举类型是值类型,可表示成未装箱和已装箱形式。然而,有别于其他值类型,枚 举类型不能定义任何方法、属性和事件。不过可利用C#的"扩展方法"功能模拟向枚举类型添加方法。

  编译枚举类型时,C#编译器会把每个符号转换成为类型的一个常量字段。例如,编译器会把前面的Color枚举类型看成以下代码:

复制代码
internal struct Color : System.Enum
{
  //以下是一些公共常量,它们定义了Color的符号和值
  public const Color While = (Color)0;
  public const Color Red = (Color)1;
  public const Color Green = (Color)2;
  public const Color Bule = (Color)3;
  public const Color Orange = (Color)4;

  //以下是一个公共实例字段,它包含一个Color变量的值,
  //不能写代码来直接引用这个实例字段
  public Int32 value__;
}
复制代码

  C#编译器实际上并不编译这段代码,因为它禁止定义从System.Enum这一特殊类型派生的类型。不过,可以通过 上述伪类型定义了解内部工作方式。简单的说,枚举类型只是一个结构,其中定义了一组常量字段和一个实例字 段。常量字段会嵌入程序集的元数据中,并可以通过反射来访问。这意味着可以在运行时获得与枚举类型关联的 所有符号及其值。还意味着可以将一个字符串符号转换成对应的数值。这些操作是通过System.Enum基类型来提 供的,该类型提供了几个静态和实例方法,可利用它们操作枚举类型的一个实例,从而避免了必须使用反射的麻 

烦。  

  提示:枚举类型定义的符号是常量值。所以当编译器发现代码引用了一个枚举类型的符号,就会在编译时用数值 替换符号,代码将不再引用定义了符号的枚举类型。这意味着在运行时可能不需要定义了枚举类型的程序集,在 

编译时需要。

  例如,System.Enum类型有一个GetUnderlyingType的静态方法,而System.Type类型有一个 GetEnumUnderlyingType的实例方法。

public static Type GetUnderlyingType (Type enumType); //System.Enum中定义
public Type GetEnumUnderlyingType (Type enumType); //System.Type中定义

  这些方法返回用于容纳一个枚举类型的值的基础类型。每个枚举类型都有一个基础类型,它可以是byte,sbyte, short,ushort,int(最常用,也是C#默认的),uint,long,或ulong。虽然这些C#基元类型都有都有对象的FCL 类型,但C#编译器为了简化本身的实现,要求只能指定基元类型名称。如果使用FCL类型名称(如Int32),就会报 

错。
  以下代码演示了如何声明一个基础类型为byte(System.Byte)的枚举类型:

复制代码
inter enum Color :byte {
    While, 
    Red, 
    Green, 
    Blue, 
    Orange 
}
复制代码

  基于这个Color枚举类型,一下代码显示了GetUnderlyingType 的返回结果:

//以下代码会显示"System.Byte"
Console.WriteLine(Enum.GetUnderlyingType(typeof(Color)));

  C#编译器将枚举类型视为基元类型。所以,可以运用许多操作符(==,!=,<,>等等)来操作枚举类型的实例。 所有这些操作符实际作用于枚举类型实例内部的value__实例字段。此外,C#编译器还运行将枚举类型的实例显式 的转型为一个不通过的枚举类型。也可以显式将一个枚举类型实例转型为一个数值类型。

  可以调用System.Enum的静态方法GetValue或者System.Type的实例方法GetEnumValue获取一个数组,该数组的每一个元素都对应枚举类型中的一个符号名称,每个元素都包含符号名称的数值:

public static Array GetValues(Type enumType); //System.Enum中定义
public Array GetEnumValues(Type enumType); //System.Type中定义

  这个方法结合ToString方法使用,可显示枚举类型中所有符号名称及其对应的数值,如下所示:

复制代码
public static void Go() {
    Color[] colors = (Color[])Enum.GetValues(typeof(Color));
    Console.WriteLine("Number of symbols defined: " + colors.Length);
    Console.WriteLine("Value\tSymbol\n-----\t------");
    foreach (Color color in colors) {
  // 以十进制和常规格式显示每个符号
    Console.WriteLine("{0,5:D}\t{0:G}", color);
    }
}
复制代码

  以上代码产生的输出如下:

Number of symbols defined: 5
Value Symbol
----- ------
0 While
1 Red
2 Green
3 Blue
4 Orange

  还有其他枚举类型成员,就不一一叙述了!

二、位标志
  程序员经常要与位标识(bit flag)集合打交道。调用System.IO.File类型的GetAttributes方法,会返回FileAttributes类型的一个实例。FileAttributes类型是基本类型为Int32的枚举类型,其中每一位都反映了文件的一项属性。FileAttibutes类型在FCL中的定义如下:

复制代码
[Flags]
[Serializable]
[ComVisible (true)]
public enum FileAttributes
{
    Archive = 0x00020,
    Compressed = 0x00800, 
    Device = 0x00040, 
    // Reserved for future use (NOT the w32 value). 
    Directory = 0x00010,
    Encrypted = 0x04000, // NOT the w32 value
    Hidden = 0x00002,
    Normal = 0x00080,
    NotContentIndexed = 0x02000,
    Offline = 0x01000,
    ReadOnly = 0x00001,
    ReparsePoint = 0x00400,
    SparseFile = 0x00200,
    System = 0x00004,
    Temporary = 0x00100,
    #if NET_4_5
    IntegrityStream = 0x8000,
    NoScrubData = 0x20000,
    #endif
}
复制代码

  为了判断一个文件是否隐藏,可执行下面这样的代码:

String file = Assembly.GetEntryAssembly().Location;
FileAttributes attributes = File.GetAttributes(file);
Console.WriteLine("Is {0} hidden? {1}",file,(attributes & FileAttributes.Hidden) !=0);

  以下代码演示了如何将一个文件的属性改为只读和隐藏:

File.SetAttributes(file,FileAttributes.ReadOnly | FileAttribute.Hidden);

  正如FileAttributes类型展示的那样,经常都要用枚举类型来表示一组可以组合的位标志。不过,虽然枚举类型和位标志相似,但它们的语义不尽相同。例如,枚举类型表示单个数值,而位标识表示一组位,其中有些位是1,有 些位是0.

  定义用于标识位标志的枚举类型时,当然应该显式为每个符号分配一个数值。通常,每个符号都有单独的一个位处于on(1)状态.此外,经常都要定义一个值为0的None符号。还可以定义一些代表常用位组合的符号。另外,强烈建议向枚举类型应用System.Flags.Attribute这个定制的attribute类型,如下所示

复制代码
[Flags] 
public enum Actions {
    Read = 0x0001,
    Write = 0x0002,
    ReadWrite = Actions.Read | Actions.Write,
    Delete = 0x0004,
    Query = 0x0008,
    Sync = 0x0010
}
复制代码

  因为Actions是枚举类型,所以在操作位标志枚举类型时,可以使用上一节描述的所有方法。

Actions actions = Actions.Read | Actions.Delete; //0x0005
Console.WriteLine(actions.ToString()); //"Read,Delete"

  调用ToString时,它会视图将数值转换为对应的符号。现在的数值是0x0005,它没有对应的符号。不过,ToString方法检测到Actions类型上存在[Flags]这个attribute,所以ToString方法现在不会将该数值视为单独的值。相反,会将它视为一组位标志。由于0x0005有0x0001和0x0004组合而成,所以ToString会生成字符串"Read,Delete", 如果从Actions类型中删除[Flags]这个attribute,ToString方法返回"5"。

  永远不要对位标志枚举类型使用IsDefined方法,理由如下:
  1)如果向IsDefined方法传递一个字符串,它不会将这个字符串拆分为单独的token来进行查找,而是视图查找整个字符串,把它看成是包含逗号的一个更大的符号。由于不能在枚举类型中定义含有逗号的符号,所以这个符号永远找不到。
  2)如果向IsDefined方法传递一个数值,它会检查枚举类型是否定义了一个其对应数值和传入数值匹配的符号。由于位标志不能这样简单匹配,所以IsDefined通常会返回flase。


三、向枚举类型添加方法
  现在,可以使用C#的扩展方法功能向枚举类型模拟添加方法。
  如果想为FileAttributes枚举类型添加一些方法,可以定义一个包含了扩展方法的静态类,如下所示:

public static Boolean Set(this FileAttributes flags, FileAttributes testFlags) {
return flags | testFlags;
}

从表面看,我似乎真的在枚举类型上调用这些方法:
FileAttributes fa = FileAttributes.System;
fa = fa.Set(FileAttributes.ReadOnly);

 


转载地址:

        http://www.cnblogs.com/zxj159/p/3566616.html

<think>好的,我现在要回答用户关于标志位与枚举的关系及用法的问题。首先,我需要回忆一下相关的知识,并结合提供的引用内容来组织答案。 首先,标志位枚举(Flags Enum)通常用于表示多个状态或选项的组合,每个枚举值对应一个位标志,通常是2的幂次方,这样可以通过位运算来组合或检查多个值。比如引用5中提到,用位标志表示星期几,工作日周末的组合。这里的关键是每个枚举值对应一个单独的位,这样使用位操作符(如OR)可以合并多个值,使用AND可以检查是否存在某个值。 接下来,我需要解释标志位枚举的基本概念,可能的结构,以及如何定义。根据引用5中的例子,每个枚举成员的值为不同的位,例如Monday是0x01(二进制00000001),Friday是0x02(00000010),这样WorkDay可以通过OR操作符组合多个成员。 然后,使用方法部分需要包括如何组合标志位,如何检查标志位是否存在。引用2中提到,使用与运算来判断是否包含某个几何类型,例如bool IsSupport = ClockWiseSupportType & Geometry.Type。这说明检查时需要用位与操作来判断结果是否为非零值。 此外,还需要提到最佳实践,比如在C#中使用[Flags]特性来修饰枚举,这样在输出时会自动处理成多个枚举值的拼接,如引用1中所说的按逗号拼接。同时,引用3提到使用工具自动生成标志位,避免手动设置容易出错的问题。 可能还需要对比普通枚举标志位枚举的区别,比如普通枚举用于单一值,而标志位用于组合。引用4中的Java枚举例子没有显式赋值,但标志位枚举需要明确每个值的位位置。 最后,总结应用场景,比如权限管理、状态集合等,如引用5中的工作日周末的例子。 现在需要确保回答结构清晰,分点说明,并正确引用相关的内容。同时,根据用户的问题,可能需要生成相关问题,比如如何定义、如何检查、应用场景等。 检查是否有遗漏的点,比如枚举的位运算操作符,或不同语言中的实现差异(C#的Flags特性,Java的处理方式)。需要指出在C#中通常用[Flags],而其他语言可能有不同的方式,但原理相同。 最后,确保使用正确的中文表达,行内公式用$...$,独立公式用$$...$$,但这个问题可能不需要数学公式,但如果有涉及位运算的示例,可能需要用LaTeX表示二进制或十六进制数,比如0x01表示为$0x01$,二进制00000001表示为$0000\ 0001$。 总结回答的结构应该是: 1. 概念解释:标志位枚举是什么,与普通枚举的区别。 2. 定义方法:如何定义每个枚举成员的值,使用2的幂。 3. 基本用法:组合(OR)、检查(AND)、移除(XOR或AND+NOT)。 4. 编程实现:以C#为例的[Flags]特性,其他语言的注意事项。 5. 应用场景:权限、状态组合等。 6. 最佳实践:使用工具生成,避免手动错误,如引用3引用5提到的。 然后根据这些点组织内容,并添加引用标记。比如在提到使用OR组合时引用15,检查时引用2,工具生成引用3,Java枚举对比引用4,应用场景引用5。 相关问题部分需要考虑用户可能想深入的问题,如如何优化、具体代码示例、不同语言的实现差异等。</think>### 标志位与枚举的关系及使用方法 #### 一、基本概念 **标志位枚举(Flags Enum)** 是一种特殊的枚举类型,用于表示多个状态或选项的**组合**。每个枚举值对应一个**二进制位标志**(即2的幂次方),通过位运算符(如与、或、异或)实现状态的组合与检查。普通枚举通常表示单一值(如星期几),而标志位枚举允许同时表示多个值(如“工作日=周一到周五”)[^5]。 #### 二、定义标志位枚举 定义时需确保每个枚举值对应**唯一的位**,通常使用十六进制或二进制表示: ```csharp [Flags] public enum FilePermissions { None = 0x00, // 0000 0000 Read = 0x01, // 0000 0001 Write = 0x02, // 0000 0010 Execute = 0x04 // 0000 0100 } ``` - **关键点**:每个值的二进制形式仅有一位为1,确保组合时不冲突[^5]。 #### 三、基本操作 1. **组合状态** 使用**按位或(|)** 运算符: ```csharp var permissions = FilePermissions.Read | FilePermissions.Write; // 0000 0011 ``` 2. **检查状态** 使用**按位与(&)** 判断是否包含某标志: ```csharp bool canWrite = (permissions & FilePermissions.Write) != 0; // true ``` 引用示例:`bool IsSupport = ClockWiseSupportType & Geometry.Type`[^2]。 3. **移除状态** 使用**按位异或(^)** 或 **按位与(&) + 按位非(~)**: ```csharp permissions = permissions & ~FilePermissions.Write; // 移除Write权限 ``` #### 四、编程实现注意事项 - **C#**:需添加 `[Flags]` 特性,系统会自动处理枚举值的拼接(如 `Read, Write`)[^1]。 - **Java**:原生不支持标志位枚举,需手动实现位运算[^4]。 - **最佳实践**:使用工具生成标志位值(如 `ioto`),避免手动赋值错误[^3]。 #### 五、应用场景 1. **权限系统**:如文件读写权限组合。 2. **状态集合**:如几何图形的支持类型(直线、曲线等组合)。 3. **配置选项**:如窗口样式(可调整大小、置顶、透明等)。 #### 六、示例代码(C#) ```csharp [Flags] public enum Days { Monday = 1 << 0, // 0000 0001 Tuesday = 1 << 1, // 0000 0010 Weekend = Saturday | Sunday } Days workDays = Days.Monday | Days.Tuesday; ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值