这种用处很大,比如权限、执行状态等,都可以用一个int型保存到数据库中,C#中使用枚举可以处理这个问题。
[Flags]
public enum Permission
{
create = 1,
read = 2,
update = 4,
delete = 8,
}
C#对该类型的操作如下:
Permission permission = Permission.create | Permission.read | Permission.update | Permission.delete;
Console.WriteLine("1、枚举创建,并赋值……");
Console.WriteLine(permission.ToString());
Console.WriteLine((int)permission);
permission = (Permission)Enum.Parse(typeof(Permission), "5");
Console.WriteLine("2、通过数字字符串转换……");
Console.WriteLine(permission.ToString());
Console.WriteLine((int)permission);
permission = (Permission)Enum.Parse(typeof(Permission), "update, delete, read", true);
Console.WriteLine("3、通过枚举名称字符串转换……");
Console.WriteLine(permission.ToString());
Console.WriteLine((int)permission);
permission = (Permission)7;
Console.WriteLine("4、直接用数字强制转换……");
Console.WriteLine(permission.ToString());
Console.WriteLine((int)permission);
permission = permission & ~Permission.read;
Console.WriteLine("5、去掉一个枚举项……");
Console.WriteLine(permission.ToString());
Console.WriteLine((int)permission);
permission = permission|Permission.delete;
Console.WriteLine("6、加上一个枚举项……");
Console.WriteLine(permission.ToString());
Console.WriteLine((int)permission);
在数据库中判断:
AND (@permission IS NULL OR @permission=0 OR permission &@permission =@permission)
上面的sql语句同样可以判断多个权限
位域主要用于.net里面对于某一个事物有多种混合状态时使用,单一的枚举更的在事物只具有单一属性时使用。为了更好的实现混合状态,我们可以在枚举加上Flags标签。下面的这个就是我们在本文中用到的实例:
[Flags]
public enum Week
{
[Description("星期一")]
Monday = 1<< 0,
[Description("星期二")]
Tuesday = 1<< 1,
[Description("星期三")]
Wednesday = 1<< 2,
[Description("星期四")]
Tursday = 1<< 3,
[Description("星期五")]
Friday = 1<< 4,
[Description("星期六")]
Saturday = 1<< 5,
[Description("星期日")]
Sunday = 1<< 6
}
位域支持的运算符
1. “|”:表示两边求并集(元素相加,相同元素只出现一次)
Week week = Week.Tuesday| Week.Monday | Week.Monday;
MessageBox.Show(Convert.ToString(week));
2. “&”:表示两边是否其中一个是另外一个的子集,如果是返回子集,否则返回0(如果其中一个包含另外一个,返回被包含的,否则返回0)
week = Week.Monday & week;
MessageBox.Show(week.ToString());
与
week = week & Week.Monday;
MessageBox.Show(week.ToString());
上面这两段代码的结果是相同的,如果week的初始值为:Monday,Tuesday,返回的结果为:Monday
3.“^”:表示从两者的并集中去除两者的交集(把两个的元素合并到一起,如果两个中有公共元素,要将这个公共元素从合并的结果中去除)
week = (Week.Monday | Week.Wednesday)^ (Week.Tuesday| Week.Monday);
MessageBox.Show(week.ToString());
week = (Week.Monday | Week.Wednesday) ^ (Week.Tuesday| Week.Sunday);
MessageBox.Show(week.ToString());
上面两个返回的结果应该为:Tuesday,Wednesday 和 Monday,Tuesday,Wednesday,Sunday
4.“~”:表示取反,返回的结果我还不知道应该是什么,以后再查一下。用法主要和“&”一起使用,例如:去除其中的某个元素
week = Week.Tuesday | Week.Monday | Week.Wednesday;
week = week&(~Week.Monday);
MessageBox.Show(week.ToString());
正逆转化
上面的内容存在数据库时我们可能为了简单只存取数字即可,例如:1表示Monday,3表示Monday,Tuesday。我们可以根据数据库里面的值方便获取存储的内容,代码如下:
week = Week.Monday | Week.Tuesday;
MessageBox.Show(Convert.ToString((int)week));
week = (Week)Enum.Parse(typeof(Week), "10");
MessageBox.Show(week.ToString());
返回的结果为:3 和 Tuesday,Tursday
获取Description标签内容
我们既然可以给里面的值加上Description,就可以在程序中获取到这个内容,至于用途,大家自己看吧,东西摆出来,大家自己随便怎么用,下面的代码是从网上找到的,内容如下:
/// <summary>
/// 从枚举类型和它的特性读出并返回一个键值对
///</summary>
/// <paramname="enumType">Type,该参数的格式为typeof(需要读的枚举类型)</param>
/// <returns>键值对</returns>
public static NameValueCollection GetNVCFromEnumValue(Type enumType)
{
NameValueCollectionnvc = new NameValueCollection();
Type typeDescription = typeof(DescriptionAttribute);
System.Reflection.FieldInfo[] fields = enumType.GetFields();
string strText = string.Empty;
string strValue = string.Empty;
foreach (FieldInfo field in fields)
{
if (field.FieldType.IsEnum)
{
strValue = ((int)enumType.InvokeMember(field.Name, BindingFlags.GetField, null, null, null)).ToString();
object[] arr =field.GetCustomAttributes(typeDescription, true);
if (arr.Length > 0)
{
DescriptionAttributeaa = (DescriptionAttribute)arr[0];
strText =aa.Description;
}
else
{
strText =field.Name;
}
nvc.Add(strText,strValue);
}
}
return nvc;
}
NET中的枚举我们一般有两种用法,一是表示唯一的元素序列,例如一周里的各天;还有就是用来表示多种复合的状态。这个时候一般需要为枚举加上[Flags]特性标记为位域,例如:
[Flags]
enum Styles{
ShowBorder = 1, //是否显示边框
ShowCaption = 2, //是否显示标题
ShowToolbox = 4 //是否显示工具箱
}
这样我们就可以用"或"运算符组合多个状态,例如:
myControl.Style = Styles.ShowBorder | Styles.ShowCaption;
这时myControl.Style枚举的值将变成 1+2=3,它的ToString()将变成"Styles.ShowBorder , Styles.ShowCaption"
这里我们可以解释为什么第三个值ShowToolbox可以为4,5..而不能为3。也就是说它的值不应该是前几项值的复合值。有一个比较简单的方法就是用2的n次方来依次为每一项赋值,例如 1,2,4,8,16,32,64.....
现在举个常见的Flags应用例子。例如一个简单的权限系统,有"Admin"和"User"两种角色,我们可以在表中放一个varchar()字段,以文本形式存放权限字"Admin,User"。但是用Flags型枚举的话,我们就可以直接将 Roles.Admin | Roles.User 的值放在一个int字段里。
以下是关于枚举的一些常见操作:
将枚举的值变回枚举对象:
Styles style = (Styles) Enum.Parse(typeof(Styles), 4 ); // -> style = Styles.Toolbox;
检查枚举是否包含某个元素:
bool hasFlag = ((style & Styles.ShowBorder) != 0);
其实我们还会碰到一种情况,就是需要从组合状态中去掉一个元素。用"^"运算符可以做到:
Styles style = Styles.ShowBorder | Styles.ShowCaption;
style = style ^ Styles.ShowBorder;
这个时候style的值就会变成 Styles.ShowCaption
但这里有一个很严重的问题(偶现在才发现)
我们这个时候再执行一次
style = style ^ Styles.ShowBorder;
按照我们的设想,这个时候 style 的值是 Styles.ShowCaption,不包含Styles.ShowBorder,所以我们就算去掉这个元素,style应该还是不会变。但实际的 style 的值却又变成了Styles.ShowBorder | Styles.ShowCaption !! 再执行一遍,又会去掉这个元素,周而复始。
当然我们可以在去掉某个元素前做一番检查,如果枚举包含这个元素,再去掉它:
if ((style & Styles.ShowBorder) != 0){
style = style ^ Styles.ShowBorder;
}
不知道有没有其它方法可以方便地从Flags枚举状态中去掉一个元素。。
Thanks to mobilebilly:
style = style & (~Styles.ShowBorder) 可以方便去掉一个元素。