枚举[Flags]位域标识位的妙用

 

这种用处很大,比如权限、执行状态等,都可以用一个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));

这段代码的结果就是 Monday,Tuesday

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());

上面返回的结果为:Tuesday,Wednesday

正逆转化

上面的内容存在数据库时我们可能为了简单只存取数字即可,例如: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) 可以方便去掉一个元素。

你可能感兴趣的:(flag)