前言
在进行权限管理时,有很多的权限属于布尔型权限,即只有“有”或“无”两种状态,在表示的时候只需要一个位(bit)。但是在基本类型中,最小的单元也是字节(byte),这样的话不仅造成很大的浪费,而且字段多了,也不便于管理。本文利用枚举型变量,使用基于位运行的策略,可以使用1个字节表示8个权限,从而解决了以上两个问题。本文同时附上C#和Java的实现。
位运行
本方法主要利用的是位运行 &
和 |
,通过两者的使用,可以分别进行 set 和 get 两种常见的操作。具体参见以下C#代码。
C#实现
static void Main()
{
// Permissions permissions = new Permissions();
// // 1.初始化权限变量 p
// Permission p = permissions.ParsePermission(0);
// permissions.ShowPermission(p);
// // 2.为 p 添加 Add 权限并显示
// p = permissions.SetPermission(p, Permission.Add, true);
// permissions.ShowPermission(p);
// // 3.为 p 添加 Edit 权限并显示
// p = permissions.SetPermission(p, Permission.Edit, true);
// permissions.ShowPermission(p);
// // 4.为 p 移除 Add 权限并显示
// p = permissions.SetPermission(p, Permission.Add, false);
// permissions.ShowPermission(p);
// // 5.为 p 添加 View 权限并显示
// p = permissions.SetPermission(p, Permission.View);
// permissions.ShowPermission(p);
// Console.WriteLine("转成整型:" + permissions.PermissionsToInt(p));
// Console.Read();
// 获得权限集合,使用int型最多可以获得31位权限(取不到32位,因为int有正负,最大值为2^31)
int a = 42442301;
// 输出2位权限,对应的输出为 “10100001111001111000111101”
Console.WriteLine(Convert.ToString(a, 2));
// 从a解析权限
Permissions p = (Permissions) Enum.Parse(typeof(Permissions), a.ToString());
// 获得某2个权限并判断,返回结果为 True, False
Console.WriteLine((p & Permissions.v0) == Permissions.v0);
Console.WriteLine((p & Permissions.v1) == Permissions.v1);
// 显示全部有的权限, 输出如下:
// Permissions granted: v0, v2, v3, v4, v5, v9, v10, v11, v12, v15, v16, v17, v18, v23, v25
Console.Write("Permissions granted: ");
foreach (Permissions item in Enum.GetValues(typeof(Permissions)))
if ((p & item) == item)
Console.Write(item + ", ");
// 直接显示p,会转换为整型,输出为:42442301
Console.WriteLine(p);
// 设置权限
p |= Permissions.v1;
// 设置权限后结果为 True
Console.WriteLine((p & Permissions.v1) == Permissions.v1);
// 保存结果,此时输出为 42442303, 由于 v1(值为2)为 True,所以增加了2。
a = (int)p;
Console.WriteLine(a);
}
// 补充几点:
// 1. Permissions.v1.ToString() 返回 v1,即枚举的值的名称字符串
// 2. (int)Permissions.v1 返回 1
// 3. 从字符串解析出 Permission 可以使用 (Permission)Enum.Parse(typeof(Permission), “v1");
// 4. 如果字符串解析失败,出跳出异常,可以使用以下办法简化操作:
// static Permission ParsePermission(string str){
// try{
// return (Permission)Enum.Parse(typeof(Permission), str);
// }
// catch { }
// return Permission.None;
// }
// 所有的值为 2^(i+1), 其中 i in [0, 30],共31位
// 在实际使用中,将v0, v1, ..., v30换成是对应的权限名称即可,如 Insert, Delete等等。
public enum Permissions:int
{
None = -1,
v0 = 1,
v1 = 2,
v2 = 4,
v3 = 8,
v4 = 16,
v5 = 32,
v6 = 64,
v7 = 128,
v8 = 256,
v9 = 512,
v10 = 1024,
v11 = 2048,
v12 = 4096,
v13 = 8192,
v14 = 16384,
v15 = 32768,
v16 = 65536,
v17 = 131072,
v18 = 262144,
v19 = 524288,
v20 = 1048576,
v21 = 2097152,
v22 = 4194304,
v23 = 8388608,
v24 = 16777216,
v25 = 33554432,
v26 = 67108864,
v27 = 134217728,
v28 = 268435456,
v29 = 536870912,
v30 = 1073741824
};
函数封装及应用
以下展示使用函数进行封装后的应用实例 。先看输出:
None
Add
Add | Edit
Edit
Edit | View
转成整型:6
以下是源代码 。
using System;
using System.Collections.Generic;
namespace ConsoleApp1
{
public enum Permission:int
{
None = 0,
Add = 1,
Edit = 2,
View = 4,
Search = 8
}
class Program
{
static void Main(string[] args)
{
// 初始化权限变量 p
Permission p = ParsePermission(0);
ShowPermission(p);
// 为 p 添加 Add 权限并显示
p = SetPermission(p, Permission.Add, true);
ShowPermission(p);
// 为 p 添加 Edit 权限并显示
p = SetPermission(p, Permission.Edit, true);
ShowPermission(p);
// 为 p 移除 Add 权限并显示
p = SetPermission(p, Permission.Add, false);
ShowPermission(p);
// 为 p 添加 View 权限并显示
p = SetPermission(p, Permission.View);
ShowPermission(p);
Console.WriteLine("转成整型:" + PermissionsToInt(p));
Console.Read();
}
/// <summary>
/// 显示权限P,如果有多个权限以|为分隔符进行显示。
/// </summary>
/// <param name="p"></param>
private static void ShowPermission(Permission p)
{
int pi = 1;
List<Permission> pms = new List<Permission>();
for (int i = 0; i < 30; i++)
{
Permission p1 = ParsePermission(pi);
if (HasPermission(p, p1))
pms.Add(p1);
pi = pi << 1;
}
if (pms.Count == 0)
pms.Add(Permission.None);
Console.WriteLine(string.Join(" | ", pms));
}
/// <summary>
/// 从一个整型变量解析出权限。
/// </summary>
/// <param name="p"></param>
/// <returns></returns>
public static Permission ParsePermission(int p)
{
return (Permission)Enum.Parse(typeof(Permission), p.ToString());
}
/// <summary>
/// 判断给定的权限是否具有某个权限。
/// </summary>
/// <param name="original"></param>
/// <param name="p"></param>
/// <returns></returns>
public static bool HasPermission(Permission original, Permission p)
{
return (original & p) == p;
}
/// <summary>
/// 为给定的权限添加或去除某个权限。
/// </summary>
/// <param name="original"></param>
/// <param name="p"></param>
/// <param name="available"></param>
/// <returns></returns>
public static Permission SetPermission(Permission original, Permission p, bool available = true)
{
return available ? original | p : original & ~p;
}
/// <summary>
/// 将指定的权限转成整型。
/// </summary>
/// <param name="original"></param>
/// <returns></returns>
public static int PermissionsToInt(Permission original)
{
return (int)original;
}
}
}
Java 实现
PS: 在Java中也一样可以用上面的方法,只不过Java的Enum类不提供相应的方法,需要自己实现。如果嫌麻烦,还有个更简单的办法,使用静态变量,代码如下所示,可以得到相似的结果。
public class Permissions {
public final static int INSERT = 1;
public final static int DELETE = 2;
public static void main(String[] args) {
int p = 42442301;
System.out.println((p & Permissions.INSERT) == Permissions.INSERT);
System.out.println((p & Permissions.DELETE) == Permissions.DELETE);
p |= Permissions.DELETE;
System.out.println((p & Permissions.DELETE) == Permissions.DELETE);
System.out.println(p);
}
}
以下是输出
true
false
true
42442303