文章开头先给大家出一道面试题:
foo = Roles.Admin | Roles.Member;var bar = foo & ~Roles.Admin;这就解决了文章前面提到的用整型来存储多角色的问题,不论数据库还是 C# 语言,操作上都是可行的,而且也很方便灵活。
枚举的 Flags 特性
下面我们提供一个通过角色来查询用户的方法,并演示如何调用,如下:
public IEnumerable<User> GetUsersInRoles(Roles roles){ _logger.LogDebug(roles.ToString()); _connection.Query<User>( "SELECT * FROM `User` WHERE `Roles` & @roles = @roles;", new { roles });}// 调用_repository.GetUsersInRoles(Roles.Admin | Roles.Member);Roles.Admin | Roles.Member 的值是 3,由于 Roles 枚举类型中并没有定义一个值为 3 的字段,所以在方法内 roles 参数显示的是 3。3 这个信息对于我们调试或打印日志很不友好。在方法内,我们并不知道这个 3 代表的是什么。为了解决这个问题,C# 枚举有个很有用的特性:FlagsAtrribute。
[Flags]public enum Roles{ Admin = 1, Member = 2}加上这个 Flags 特性后,我们再来调试 GetUsersInRoles(Roles roles) 方法时,roles 参数的值就会显示为 Admin|Member 了。简单来说,加不加 Flags 的区别是:
var roles = Roles.Admin | Roles.Member;Console.WriteLing(roles.ToString()); // "3",没有 Flags 特性Console.WriteLing(roles.ToString()); // "Admin, Member",有 Flags 特性
给枚举加上 Flags 特性,我觉得应当视为 C# 编程的一种最佳实践,在定义枚举时尽量加上 Flags 特性。
解决枚举值冲突:2 的幂
到这,枚举类型 Roles 一切看上去没什么问题,但如果现在要增加一个角色:Mananger,会发生什么情况?按照数字值递增的规则,Manager 的值应当设为 3。
[Flags]public enum Roles{ Admin = 1, Member = 2, Manager = 3}能不能把 Manager 的值设为 3?显然不能,因为 Admin 和 Member 进行位的或逻辑运算(即:Admin | Member) 的值也是 3,表示同时拥有这两种角色,这和 Manager 冲突了。那怎样设值才能避免冲突呢?既然是二进制逻辑运算“或”会和成员值产生冲突,那就利用逻辑运算或的规律来解决。我们知道“或”运算的逻辑是两边只要出现一个 1 结果就会 1,比如 1|1、1|0 结果都是 1,只有 0|0 的情况结果才是 0。那么我们就要避免任意两个值在相同的位置上出现 1。根据二进制满 2 进 1 的特点,只要保证枚举的各项值都是 2 的幂即可。比如:
1: 000000012: 000000104: 000001008: 00001000
再往后增加的话就是 16、32、64...,其中各值不论怎么相加都不会和成员的任一值www.easck.com冲突。这样问题就解决了,所以我们要这样定义 Roles 枚举的值:
[Flags]public enum Roles{ Admin = 1, Member = 2, Manager = 4, Operator = 8}不过在定义值的时候要在心中小小计算一下,如果你想懒一点,可以用下面这种“位移”的方法来定义:
[Flags]public enum Roles{ Admin = 1 << 0, Member = 1 << 1, www.easck.com Manager = 1 << 2, Operator = 1 << 3}一直往下递增编值即可,阅读体验好,也不容易编错。两种方式是等效的,常量位移的计算是在编译的时候进行的,所以相比不会有额外的开销。
总结
本文通过一道小小的面试题引发一连串对枚举的思考。在小型系统中,把用户角色直接存储在用户表是很常见的做法,此时把角色字段设为整型(比如 int)是比较好的设计方案。但与此同时,也要考虑到一些最佳实践,比如使用 Flags 特性来帮助更好的调试和日志输出。也要考虑到实际开发中的各种潜在问题,比如多个枚举值进行或(‘|’)运算与成员值发生冲突的问题。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。








