最近在利用这个枚举做一些状态管理,遇到了int强转枚举的情况。但有时候内部情况还是不太清楚,因此,研究了这个强转的过程,以及标记了Flags的枚举的强转。
public enum expEnum
{
p0, //首项不指定值,则默认 =0
p1, //不指定值,则默认为上一项 +1,此处 =1
p2, //同上,默认 +1=2
q1=1 //值可以重复,此处显式指定 q1=1
q2, //同上,默认 +1=2
q1=2, //error: 类型已包含 q1 的定义
a1=1,
}
……
int status=1;
expEnum StatusEnum=(expEnum)status;
// 此时可以看到 StatusEnum==expEnum.a1 依字幕排序,a1 是排在 p1 和q1 前面,所以优先匹配。
[Flags]
public enum expFlagsEnum
{
p0,//p0=0, 首项不指定值,依然是从 0 开始
p1,//p1=1, 默认 +1
p2,//p2=2
p4=4,
p8=8,
}
……
int status=7;
expFlagsEnum StatusEnum=(expFlagsEnum)status;
// 转换后 可以看到 StatusEnum==expFlagsEnum.p1|expFlagsEnum.p2|expFlagsEnum.p4
……
//[Flags]---------------------------------去掉 Flags 标记
public enum expFlagsEnum
{
p0,//p0=0, 首项不指定值,依然是从0开始
p1,//p1=1, 默认+1
p2,//p2=2
p4=4,
p8=8,
}
……
int status=7;
expFlagsEnum StatusEnum=(expFlagsEnum)status;
// 转换后 可以看到 StatusEnum==7
……
网上的资料一般都不涉及这种特殊情形,只讨论是2的整数幂即:1,2,4的情形,而不讨论非2的整数幂的情况。
这里的特殊主要特殊在从int转Enum的时候,比较复杂。有这么几个原则:
1、最大值优先
//================最大匹配原则================
[Flags]
public enum expFlagsEnum
{
p0,//p0=0, 首项不指定值,依然是从 0 开始
p1,//p1=1, 默认 +1
p2,//p2=2
p4=4,
p8=8,
p16=16,
p31=31,//非 2 的整数幂
p32=32
}
……
int status=47;
expFlagsEnum StatusEnum=(expFlagsEnum)status;
// 转换后 可以看到 StatusEnum==expFlagsEnum.p1|expFlagsEnum.p2|expFlagsEnum.p4|expFlagsEnum.p8|expFlagsEnum.p32
……
//47 有多种匹配方式,例如:
//47 = 32+8+4+2+1
//47 = 31+16
//根据最大匹配原则,优先匹配的是包含 32 的方案,尽管 31+16 看上去更简洁
2、按位拆分
//================最大匹配原则================
[Flags]
public enum expFlagsEnum
{
p0,//p0=0, 首项不指定值,依然是从 0 开始
p1,//p1=1, 默认 +1
p2,//p2=2
p3,
p8=8,
}
……
int status=6;
expFlagsEnum StatusEnum=(expFlagsEnum)status;
// 转换后 可以看到 StatusEnum==6 即没有转为枚举值,直接保留了 int
……
//直觉上,应该是 6=3+2+1
// 但实际上6表示为二进制即为 110,即表示为 6=100+010,于是6只能匹配 4+2 或者6自身
// 再举例
[Flags]
public enum expFlagsEnum
{
p0,//p0=0, 首项不指定值,依然是从 0 开始
p1,//p1=1, 默认 +1
p2,//p2=2
p3,
p4=4,
p8=8,
}
int status=7;
expFlagsEnum StatusEnum=(expFlagsEnum)status;
// 转换后 可以看到 StatusEnum==expFlagsEnum.p4|expFlagsEnum.p3
//拆分后的两个数字,在转换为“二进制”时,每一位上不能有重叠。
// 7=111 可以分成 100和011 这两个数字在每一位上的 1 不重叠,于是7 可以重组成4+3
// 至于为什么是 4+3而不是 4+2+1 上面已述,是最大值原则。
//
可以看到这样的枚举定义为Flags情况会比较复杂,因此上,一般而言,标注了Flags,其项都是用2的整数幂,这样是比较清晰的定义。
而一个复杂用法的例子就是Keys,标记了Flags,主要是为了任意键和Ctrl,Alt,Shift的组合。这三个键定义的是2的整数幂,而且是大整数,以保证不会有其他枚举项与之有重叠。
至于其他键,则不是很建议将其合并处理。合起来常常会产生冲突。
比如:A=65,B=66 按位或起来是67,成了Keys.C=67键了。
namespace System.Windows.Forms
{
[System.Flags]
public enum Keys
{
....
}
}