关于c#:C面向对象枚举和位运算

2次阅读

共计 4494 个字符,预计需要花费 12 分钟才能阅读完成。

大家在学习 DateTime 的时候有没有发现一个 DayOfWeek 类型,示意星期几。应用 F12 就能够看到它的定义:

这里就呈现了:

枚举(Enum)

枚举(enum)是和类(class)、构造(struct)并列的一种类型。它的成员又被称之为枚举值,枚举值不能有任何修饰符,只能蕴含两局部:

  • 名称,比方 DayOfWeek 中的 Sunday、Monday 等,名称个别是“有意义的”,以不便开发人员调用
  • 底层数据,比方 DayOfWork 中的 0、1、2 等。默认底层数据是 int 类型,还能够应用其余整数类型,比方 byte、short 等,如下所示:

    enum Grade : short // 默认是 int 类型,当初是 short 类型 {}

底层数据也能够不写,不写的话默认从 0 开始,从上到下顺次减少 1:

enum Grade : short // 默认是 int 类型 {Excellent, // 不写底层数据默认为 O Passed, // 默认以 1 的步长递增底层数据 Failed = 8 // 指定底层数据}

枚举能够像类型一样应用:

// 申明 Grade 属性 public Grade grade {get; set;} // 作为办法的返回值 static Grade GetBy(int score){} // 作为办法的参数 static int GetBy(Grade score){}

它的值间接枚举名点(.)出即可:

// 申明变量 / 字段 Grade grade = Grade.Excellent;

因为枚举的整型底层数据,所以能够在枚举和整型之间进行强制转换:

Console.WriteLine((short)Grade.Excellent); //int 类型也行(能够和 short 转换)Console.WriteLine((int)Grade.Passed); Console.WriteLine((int)Grade.Failed); Console.WriteLine((Grade)0); //Excellent Console.WriteLine((Grade)16); // 无奈转换时也不会报错,间接输入 16

留神:枚举的默认值是:

  • 0 所对应的枚举值,或者
  • 如果找不到 0 所对应的枚举值,就间接为 0

演示:

为什么要应用枚举呢?

以 DayOfWork 为例,如果没有枚举,咱们如何传递“星期几”呢?比方咱们有一个办法,能够依据星期几返回 bool 值,如果是星期三和星期六,就返回 true,示意当天不上课,^_^

怎么申明这个办法呢?参数用什么类型?int,string?比方这样:

static bool IsRest(int dayofWork) {return dayofWork == 3 || dayofWork == 6;}

这样写,有两个问题:

  1. 开发人员怎么晓得 3 代表星期三,6 代表星期六?你说这个还能够对应着来,星期天你怎么示意?0,还是 7?不同的文化可能有不同的定义,这就是问题。
  2. 有没有可能传入一个参数:8?因为 int 类型的取值是没有限度的。

@想一想 @:如果用 string 类型呢?

所以咱们用 DayOfWork 的枚举来做:

static bool IsRest(DayOfWeek dayofWork) {return dayofWork == DayOfWeek.Wednesday || dayofWork == DayOfWeek.Saturday;}

首先,DayOfWeek.Wednesday 表义十分分明;而后,参数是枚举类型,所以你没方法传入一个不存在的枚举值的。这些,都极大的进步了代码的可读性和健壮性(少 bug)。

枚举通常和

switch…case

配合应用,比方:

Grade grade = Grade.Excellent; switch (grade) {case Grade.Excellent: Console.WriteLine(“ 发 10 个红包 ”); break; case Grade.Passed: Console.WriteLine(“ 发 5 个红包 ”); break; case Grade.Failed: Console.WriteLine(“ 没有红包了 ”); break; default: Console.WriteLine(“ 怎么回事?”); break; }

整段代码的意思就是,依据 grade 的值进行分支:

  1. 如果 grade 的值等于 Grade.Excellent,输入 ” 发 10 个红包 ”;
  2. 如果 grade 的值等于 Grade.Passed,输入 ” 发 5 个红包 ”;
  3. 如果 grade 的值等于 Grade.Failed,输入 ” 没有红包了 ”;
  4. 如果 grade 的值不等于下面的任何的一个枚举值,输入 ” 怎么回事?”

这种逻辑其实用 if…else… 也能够实现,然而 switch…case 看起来更整洁一些。但要留神以下几点:

  • 只能进行“等值”运算,也就是说只能判断 switch() 中的值是否等于 case 前面的值,不能进行大于小于等其余运算。
  • 代码执行时从上往下比拟,所以程序很重要。尤其是 default,相当于 if…else… 中最初兜底的 else,应该是在上述所有 case 条件都不满足的状况下才执行的,所以只能放在最初。同时强烈建议总是带上 default,哪怕用于报异样!
  • 不要遗记 break,break 意味着跳出 switch 域。如果两个 case 之间没有 break 只有业务逻辑语句,就会报编译谬误;如果两(多)个 case 之间没有任何其余语句,这些 case 条件形成一种“或”的关系,比方下面周三周六劳动的需要咱们就能够这样实现:

    static bool IsRest(DayOfWeek dayofWork) {switch (dayofWork) {case DayOfWeek.Saturday: case DayOfWeek.Thursday: return true; // 此处能够用 return 代替 break default: return false;} }

因为枚举的底层数据是整数类型,它还能够进行

位运算

首先要把传入的数转化成二进制,而后按二进制对齐,对每一个雷同的位的值进行相应运算——这就被称之为位运算。

咱们这里介绍三种位运算:

  • &:读作“位与”,只有两个 1 才为 1
  • |:读作“位或”,只有一个 1 就为 1
  • ^:读作“异或”,两个值雷同才为 1

    Console.WriteLine(1 | 2); // 01 | 10 => 11 (3) Console.WriteLine(2 | 4); // 010 | 100 => 110 (6) Console.WriteLine(1 & 2); // 01 & 10 => 00 (0) Console.WriteLine(2 & 4); // 010 & 100 => 000 (0) Console.WriteLine(1 ^ 2); // 01 ^ 10 => 11 (3) Console.WriteLine(2 ^ 4); // 010 ^ 100 => 110 (6)

(除 | 和 & 以外,还有 >< 等位移运算、补位运算,我的项目开发中用得非常少,这里不予讲述)

常见面试题:| 和 ||,& 和 && 的区别

当 | 和 & 利用于逻辑运算时,失去的后果同 || 和 && 是统一的。然而后者是有短路操作进行优化的。具体来说,是指:在组合逻辑运算中,一旦可能失去最终后果,就立刻终止运算,间接返回后果。比方:

  • 在逻辑或运算中,只有有一个条件为真,整个运算后果必然为真;
  • 在逻辑且运算中,只有有一个条件为假,整个运算后果必然为假,

就不必再计算前面的条件了。

static bool condition1() { Console.WriteLine(“condition1()…”); return true; // 或者 false } static bool condition2() { Console.WriteLine(“condition2()…”); return false; // 或者 true } Console.WriteLine(condition1() | condition2()); Console.WriteLine(condition1() || condition2()); Console.WriteLine(condition1() & condition2()); Console.WriteLine(condition1() && condition2());

断点演示:短路运算时相应 condition 不会被运行……

法则

如果所有参加运算成员的数值都是:

  • 2 的整数次方,比方 1、2、4、8、16……,或者
  • 2 的整数次方数之和,比方 3(=1+2)、6(=2+4)、7(=1+2+4)……

他们就会有如下法则:

  • 位或(|)相当于“加”,比方:2|4=6,2|4|1=7……,以下咱们将位或(相加)的后果简称为“位或后果”,参加位或运算的成员简称为“成员”,未参加运算的称之为“非成员”
  • 而后,将位或后果和任一成员进行异或(^)运算,其成果就相当于“减”,比方:6^2=4,7^4=3,
  • 最初,将位或后果和任一成员进行位与(&)运算,其后果就等于该成员,比方:6&2=2,7&4=4;且与任何非成进行位与(&)运算,其后果不会等于该非成员,比方:6&1=0,7&8=0

严格的数学证实,飞哥也不会,^_^,同学们就这样记住论断吧……

@想一想 @,这能够用来干嘛?

作用

进行权限治理啊!

比方源栈的学生,能够是:

  • 学生(Student),还能够是
  • 老师助理(TeacherAssist),以及
  • 小组长(TeamLeader)和
  • 寝室长(DormitoryHead)

每个同学能够是下面一种或多种角色,而且能够随时增减。让你在 Student 类中增加一个属性记录同学的角色,你怎么办?常见的会用数组(或者当前咱们会学习的汇合),然而,更“业余”更有“逼格”的办法是:

应用枚举(即整数)代表角色,如下所示:

enum Role {Student = 1, TeacherAssist = 2, TeamLeader = 4, DormitoryHead = 8}// 留神所有枚举值必须是 2 的整数次方

而后对他们进行位运算:

  • |:增加权限
  • ^:剥夺权限
  • &:检测用户是否具备某项权限

    //Role 为 int 类型 public int Role {get; set;} Student tlzz = new Student {Role = (int)Role.Student | (int)Role.TeamLeader }; tlzz.Role = tlzz.Role | (int)Role.DormitoryHead; tlzz.Role = tlzz.Role ^ (int)Role.Student; Console.WriteLine((tlzz.Role & (int)Role.TeamLeader) == (int)Role.TeamLeader);

作业:

  1. 申明一个令牌(Token)枚举,蕴含值:SuperAdmin、Admin、Blogger、Newbie、Registered。
  2. 申明一个令牌治理(TokenManager)类:
  3. 应用公有的 Token 枚举_tokens 存储所具备的权限
  4. 裸露 Add(Token)、Remove(Token) 和 Has(Token) 办法,能够增加、删除和判断其有无某个权限
  5. User 类中增加一个 Tokens 属性,类型为 TokenManager
正文完
 0