文章开头先给大家出一道面试题:
在设计某小型项目的数据库(假设用的是 MySQL)时,如果给用户表(User)添加一个字段(Roles)用来存储用户的角色,你会给这个字段设置什么类型?提示:要考虑到角色在后端开发时需要用枚举表示,且一个用户可能会拥有多个角色。
映入你脑海的第一个答案可能是:varchar 类型,用分隔符的方式来存储多个角色,比如用 1|2|3 或 1,2,3 来表示用户拥有多个角色。当然如果角色数量可能超过个位数,考虑到数据库的查询方便(比如用 INSTR 或 POSITION 来判断用户是否包含某个角色),角色的值至少要从数字 10 开始。方案是可行的,可是不是太简单了,有没有更好的方案?更好的回答应是整型(int、bigint 等),优点是写 SQL 查询条件更方便,性能、空间上都优于 varchar。但整型毕竟只是一个数字,怎么表示多个角色呢?此时想到了二进制位操作的你,心中应该早有了答案。且保留你心中的答案,接着看完本文,或许你会有意外的收获,因为实际应用中可能还会遇到一连串的问题。为了更好的说明后面的问题,我们先来回顾一下枚举的基础知识。
枚举基础
枚举类型的作用是限制其变量只能从有限的选项中取值,这些选项(枚举类型的成员)各自对应于一个数字,数字默认从 0 开始,并以此递增。例如:
public enum Days { Sunday, Monday, Tuesday, // ... }
其中 Sunday 的值是 0,Monday 是 1,以此类推。为了一眼能看出每个成员代表的值,一般推荐显示地将成员值写出来,不要省略:
public enum Days { Sunday = 0, Monday = 1, Tuesday = 2, // ... }
C# 枚举成员的类型默认是 int 类型,通过继承可以声明枚举成员为其它类型,比如:
public enum Days : byte { Monday = 1, Tuesday = 2, Wednesday = 3, Thursday = 4, Friday = 5, Saturday = 6, Sunday = 7 }
枚举类型一定是继承自 byte、sbyte、short、ushort、int、uint、long 和 ulong 中的一种,不能是其它类型。下面是几个枚举的常见用法(以上面的 Days 枚举为例):
// 枚举转字符串 string foo = Days.Saturday.ToString(); // "Saturday" string foo = Enum.GetName(typeof(Days), 6); // "Saturday" // 字符串转枚举 Enum.TryParse("Tuesday", out Days bar); // true, bar = Days.Tuesday (Days)Enum.Parse(typeof(Days), "Tuesday"); // Days.Tuesday // 枚举转数字 byte foo = (byte)Days.Monday; // 1 // 数字转枚举 Days foo = (Days)2; // Days.Tuesday // 获取枚举所属的数字类型 Type foo = Enum.GetUnderlyingType(typeof(Days))); // System.Byte // 获取所有的枚举成员 Array foo = Enum.GetValues(typeof(MyEnum); // 获取所有枚举成员的字段名 string[] foo = Enum.GetNames(typeof(Days));
另外,值得注意的是,枚举可能会得到非预期的值(值没有对应的成员)。比如:
Days d = (Days)21; // 不会报错 Enum.IsDefined(typeof(Days), d); // false
即使枚举没有值为 0 的成员,它的默认值永远都是 0。
var z = default(Days); // 0
枚举可以通过 Description、Display 等特性来为成员添加有用的辅助信息,比如:
public enum ApiStatus { [Description("成功")] OK = 0, [Description("资源未找到")] NotFound = 2, [Description("拒绝访问")] AccessDenied = 3 } static class EnumExtensions { public static string GetDescription(this Enum val) { var field = val.GetType().GetField(val.ToString()); var customAttribute = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)); if (customAttribute == null) { ret<strong style="color:transparent">本文来源gao@daima#com搞(%代@#码网@</strong>urn val.ToString(); } else { return ((DescriptionAttribute)customAttribute).Description; } } } static void Main(string[] args) { Console.WriteLine(ApiStatus.Ok.GetDescription()); // "成功" }