.NET/C#基础之枚举类型转换、标志位枚举[Flags]及常用位操作

491 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第21天,点击查看活动详情

C#值类型中的枚举类型,我们需要了解它与int、string类型的转换,以及使用[Flags]特性启用位枚举的方式,在选项组合中非常实用。

值类型

枚举类型

枚举类型转换

Enum、Int和String的互相转换。

Enum ——> String

  • Object.ToString():获取字符串
Season.Spring.ToString();   // "Spring"
  • Enum的静态方法GetNameGetNames
Enum.GetName(typeof(Season),1);      // Summer

Enum.GetName(typeof(Season), Season.Summer);     // Summer
  • Enum.GetNames(typeof(Season) 获取枚举(成员)的字符串数组。

String ——> Enum

  • Enum的静态方法ParseTryParse
var season=Enum.Parse(typeof(Season),"Autumn",ignoreCase:true);
var result = Enum.TryParse(typeof(Season), "Autumn", ignoreCase: true, out object? season1);
var result2 = Enum.TryParse("Autumn", ignoreCase: true, out Season season2); // 推荐

Enum ——> Int

  • 强制类型转换

枚举的基类型是除 Char 外的整型,所以可以进行强制转换。

Console.WriteLine((int)Season.Summer);
Console.WriteLine((byte)Season.Summer);

Int ——> Enum

  • 强制将整型转换成枚举类型
var season = (Season)2;
  • Enum的静态方法ToObject
var season3 = Enum.ToObject(typeof(Season),3);
Console.WriteLine(season3); // Winter

ToObject() 得到的是object类型。

Enum.IsDefined 判断某个整型或名称是否定义在枚举中

Console.WriteLine(Enum.IsDefined(typeof(Season), 1));   // True
Console.WriteLine(Enum.IsDefined(typeof(Season), "Autumn")); // True

[Flags] 特性的位枚举/标志枚举

HasFlag() 方法可以判断当前实例是否设置了标志位(或 位域)。

位枚举的介绍和声明

[Flags] 特性可以将枚举类型转变为位标记(bit flags)的枚举。

其本质,是将枚举值对应到二进制数值位上,比如第一个成员值对应为0,第二个成员值对应为1,第三个对应为2 .... 每个二进制位表示一个枚举值(2的二次幂),如下所示:

[Flags]
public enum Days
{
    None      = 0b_0000_0000,  // 0
    Monday    = 0b_0000_0001,  // 1
    Tuesday   = 0b_0000_0010,  // 2
    Wednesday = 0b_0000_0100,  // 4
    Thursday  = 0b_0000_1000,  // 8
    Friday    = 0b_0001_0000,  // 16
    Saturday  = 0b_0010_0000,  // 32
    Sunday    = 0b_0100_0000,  // 64
    Weekend   = Saturday | Sunday
}

这就是位枚举/标志枚举,由于每个值表示一个二进制位,所以,位枚举可以执行 按位或|、按位与& 操作,组合不同的取值。比如上面的Weekend就是Saturday | Sunday的组合值。

位枚举的主要应用就是选择的组合(复合选项取值)。

[Flags]特性指定位枚举时,应该同时指定枚举的整数值,类似上面的写法,因为默认枚举值是从0按顺序递增的。

如果不指定整数值,默认的位枚举会表示为如下:

[Flags]
public enum Permission
{                                 
  Unknown,   // 0        
  Create,    // 1      
  Read,        // 2
  Update,      // Permission.Create | Permission.Read -- 3
    Delete,      // 4
}

如果要指定枚举取值,还可以直接赋值十进制值 0,1,2,4,8....,或者使用 按位左移操作符<<,对二进制的个位1进行左移:

[Flags]
public enum Permission
{
    Unknown = 0, // 也可以写成0x00或0           
    Create = 1 << 0, // 0x01或1         
    Read = 1 << 1,  //0x02或2
    Update = 1 << 2, //0x04或4
    Delete = 1 << 3,  //0x08或8
    ReadWrite= Permission.Read | Permission.Update
}

位枚举实例

Permission位枚举为例,下面通过 位与&、或| 等操作,查看如何组合和管理 位枚举 的不同取值。

  • 1、|(组合): 给用户创建、读取,修改和删除的权限
var permission = Permission.Create | Permission.Read | Permission.Update | Permission.Delete;
  • 2、~按位取反再&与(交叉):去掉用户的修改和删除权限
permission = permission & ~Permission.Update;
permission = permission & ~Permission.Delete;
  • 3、| 在原来枚举实例上,增加位枚举取值
permission = permission | Permission.Update;
  • 4、&与:判断是否有某个位枚举值。

判断用户是否有创建的权限

var isCreate = (permission & Permission.Create)!=0;
//或者
var isCreate = (permission & permission.Create)==permission.Create;

parmission枚举的值如果为 0+1+4=5parmission.ToSting()将变成"Create, Update"

在引用COM组件时,也需要用到位枚举。

不加 [Flags] 特性的普通枚举实例也可以按位操作,那就是枚举实例对应的整数值的位操作了。

位枚举的位操作【总结】

  • add 添加
a|b
  • remove 删除
a & ~b
  • check 检查是否存在
(a & b ) != 0;
  • reverse 翻转(有就删,无就加)
a ^ b

参考