我们重点来讲解 简单枚举和标志枚举的用法和区别

继承

Object-> ValueType ->Enum
Object-> ValueType ->struct 包括int float等简单值类型
Object-> ValueType ->ValueTuple
Object-> ValueType ->Nullable

枚举的简介:

1、枚举类型 是由基础整型数值类型的一组命名常量定义的值类型

2.枚举使用enum关键字来声明, 枚举可以和类并列也可以  写在类里面,不能写在方法里。

namespace ConsoleApp1
{
class Program
{
//////////////省略。。。。。。。。。
}
[Flags]
enum Man
{
//////////////省略。。。。。。。。。
}
}

3、枚举都是隐式密封的(sealed),不允许作为基类派生子类。枚举里面的成员只能是默认公共的静态的(public、static),不能有访问修饰符;枚举本身一般不加访问修饰符,要加的话只能是internal

或public;

4、枚举成员是常量 不可更改。可以当作字段来用。由于枚举成员在编译时将被替换为字面量,使得其成员取值类型受到了一定限制。

public readonly int SOU = 12;
public const int seiu = 12;
enum n { i=0}
编译后的iL代码
.field public initonly int32 SOU
.field public static literal int32 seiu = int32(12)
.field public static literal valuetype Galaxy.Program/n i = int32(0)
//枚举中字段IL代码和Const 常量定义的IL代码是一样的,编译时常数,在编译时是已知的,在程序的生命周期内不会改变
5、默认情况下,枚举成员的关联常数值为类型 int(被编译查成IL:.field public specialname rtspecialname int32 value__) ;它们从零开始,并按定义文本顺序递增 1。 可以显式指定任何
其他整数数值类型作为枚举类型的基础类型。 还可以显式指定关联的常数值,如下面的示例所示:

enum ErrorCode : int  //不是继承,只是写法像继承,查看IL代码 被编译成.field public specialname rtspecialname int32 value__,如果是继承应该显示extends
{
None = 0,
Unknown = 1,
ConnectionLost = 100,
OutlierReading = 200
}
6、枚举元素不可以重复,枚举元素的值可以重复。最后一个元素后的逗号可有可无。枚举最后的冒号可有可无。
7、 基础类型指定为每个枚举数分配的存储大小。
 

System.Enum 类型和枚举约束

System.Enum 类型是所有枚举类型的抽象基类。 它提供多种方法来获取有关枚举类型及其值的信息。 有关更多
信息和示例,请参阅 System.Enum API 参考页。
从 C# 7.3 开始,你可以在基类约束中使用 System.Enum (称为枚举约束),以指定类型参数为枚举类型。 所有枚举
类型也都满足 struct 约束,此约束用于指定类型参数为不可为 null 的值类型。

转换

对于任何枚举类型,枚举类型与其基础整型类型之间存在显式转换。 如果将枚举值转换为其基础类型,则结果为
枚举成员的关联整数值。

public enum Season
{
Spring,
Summer,
Autumn,
Winter
}
public class EnumConversionExample
{
public static void Main()
{
Season a = Season.Autumn;
Console.WriteLine($"Integral value of {a} is {(int)a}"); // output: Integral value of Autumn is 2
var b = (Season)1;
Console.WriteLine(b); // output: Summer
}
}

使用 Enum.IsDefined 方法来确定枚举类型是否包含具有特定关联值的枚举成员。
对于任何枚举类型,都存在分别与 System.Enum 类型的装箱和取消装箱相互转换。

1、简单枚举:枚举变落表示一个成员;

枚举类型的作用是限制其变量只能从有限的选项中取值,这些选项(枚举类型的成员)各自对应于一个数字,数字默认从 0 开始,并以此递增。例如:

public enum Days
{
Sunday, Monday, Tuesday, // ...
}

其中 Sunday 的值是 0,Monday 是 1,以此类推。为了一眼能看出每个成员代表的值,一般推荐显示地将成员值写出来,不要省略:

public enum Days
{
Sunday = 0, Monday = 1, Tuesday = 2, // ...
}

C# 枚举成员的类型默认是 int 类型,通过继承可以声明枚举成员为其它类型,比如:

publicenum Days : byte
{
Monday = 1,
Tuesday = 2,
Wednesday = 3,
Thursday = 4,
Friday = 5,
Saturday = 6,
Sunday = 7
}

枚举类型一定是继承自 byte、sbyte、short、ushort、int、uint、long 和 ulong 中的一种,不能是其它类型。下面是几个枚举的常见用法(以上面的 Days 枚举为例):

// 枚举转字符串
string foo = Days.Saturday.ToString(); // "Saturday"
string foo = Enum.GetName(typeof(Days), 6); // "Saturday"
// 字符串转枚举
Enum.TryParse("Tuesday", out Days bar); // true, bar = Days.Tuesday
(Days)Enum.Parse(typeof(Days), "Tuesday"); // Days.Tuesday

// 枚举转数字
byte foo = (byte)Days.Monday; // 1
// 数字转枚举
Days foo = (Days)2; // Days.Tuesday

// 获取枚举所属的数字类型
Type foo = Enum.GetUnderlyingType(typeof(Days))); // System.Byte

// 获取所有的枚举成员
Array foo = Enum.GetValues(typeof(MyEnum);
// 获取所有枚举成员的字段名
string[] foo = Enum.GetNames(typeof(Days));

另外,值得注意的是,枚举可能会得到非预期的值(值没有对应的成员)。比如:

Days d = (Days)21; // 不会报错
Enum.IsDefined(typeof(Days), d); // false

即使枚举没有值为 0 的成员,它的默认值永远都是 0。

var z = default(Days); // 0

枚举可以通过 Description、Display 等特性来为成员添加有用的辅助信息,比如:

public enum ApiStatus
{
[Description("成功")]
OK = 0,
[Description("资源未找到")]
NotFound = 2,
[Description("拒绝访问")]
AccessDenied = 3
} static class EnumExtensions
{
public static string GetDescription(this Enum val)
{
var field = val.GetType().GetField(val.ToString());
var customAttribute = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute));
if (customAttribute == null) { return val.ToString(); }
else { return ((DescriptionAttribute)customAttribute).Description; }
}
} static void Main(string[] args)
{
Console.WriteLine(ApiStatus.Ok.GetDescription()); // "成功"
}

上面这些我认为已经包含了大部分我们日常用到的枚举知识了。下面我们继续回到文章开头说的用户角色存储问题。

2、标记枚举:枚举变量可以表示一个集合,每当可枚举表示可能值的集合而不是单个值时,应使用[Flags]属性

2.1 标志枚举的使用准则

避免创建标志枚举,其中某些值的组合无效。

避免使用值为零的标记枚举值,除非该值表示 "所有标志均已清除" 并正确命名,如下一个准则所述。

️ DO name 标记枚举的零值 None 。 对于标志枚举,值始终为 "清除所有标志"。

2.2三种写法都是等效的;

第一种写法:位数大了以后不容易察觉错误;

第二中写法:推荐,容易理解 标志枚举是如何运作的。

第三种写法:容易阅读,但是有个bug,当移位超出整形限制范围时候,编译器不会报错,计算结果失效。曹操不是兄弟,但是在int整型范围之下 1<<62 和1<<30 值是一样的,导致计算结果出错。

static void Main(string[] args)
{
Man xiongdi = Man.关羽 | Man.刘备 | Man.张飞; if((xiongdi.HasFlag(Man.曹操)))
Console.WriteLine($"{Man.司马懿}是兄弟");//司马懿是兄弟
if ((xiongdi.HasFlag(Man.关羽)))
Console.WriteLine($"{Man.关羽}是兄弟");//关羽是兄弟 }
[Flags]
enum Man
{
曹操=1<<30,//IL反编译显示:曹操 = 0x40000000,
司马懿 =1<<31,//IL反编译显示:司马懿 = int.MinValue,
刘备 =1<<32,//IL反编译显示:刘备 = 0x1,
关羽 =1<<33,//IL反编译显示:关羽 = 0x2,
张飞 =1<<62,//IL反编译显示:张飞 = 0x40000000 }

每当可枚举表示可能值的集合而不是单个值时,应使用[Flags]属性。此类集合通常与按位运算符一起使用,例如:

var allowedColors = MyColor.Red | MyColor.Green | MyColor.Blue;
 

请注意,[Flags]属性本身并不启用此功能,它所做的只是允许.ToString()方法的良好表示:

enum Suits { Spades = 1, Clubs = 2, Diamonds = 4, Hearts = 8 }
[Flags] enum SuitsFlags { Spades = 1, Clubs = 2, Diamonds = 4, Hearts = 8 } ... var str1 = (Suits.Spades | Suits.Diamonds).ToString();
//"5"
var str2 = (SuitsFlags.Spades | SuitsFlags.Diamonds).ToString();
//"Spades, Diamonds"
 

还需要注意的是,[Flags]不会自动使枚举值的幂为2。如果省略数字值,枚举将不会像在按位操作中预期的那样工作,因为默认情况下,值以0和增量开头。

          Console.WriteLine(default(Man)); //枚举的默认值都是0

声明不正确:

[Flags]
public enum MyColors
{
Yellow, // 0
Green, // 1
Red, // 2
Blue // 3
}
 

如果以这种方式声明,值将为黄色=0、绿色=1、红色=2、蓝色=3。这将使其作为标志无效。

以下是正确声明的示例:

[Flags]
public enum MyColors
{
Yellow = 1,
Green = 2,
Red = 4,
Blue = 8
}

2.3位枚举使用案例

//1、给用户创建、读取,修改和删除的权限
var parmission = Permission.Create | parmission.Read | parmission.Update | parmission.Delete; //2、去掉用户的修改和删除权限
parmission = parmission &~parmission.Update;
parmission = parmission &~parmission.Delete; //3、给用户加上修改的权限
parmission = parmission | parmission.Update; //4、判断用户是否有创建的权限
var isCreate =(myProperties.AllowedColors.HasFlag(MyColor.Yellow);
 

或在.NET 4之前:

if((myProperties.AllowedColors & MyColor.Yellow) == MyColor.Yellow)
{
// Yellow is allowed...
} if((myProperties.AllowedColors & MyColor.Green) == MyColor.Green)
{
// Green is allowed...
}

3、简单枚举和标记枚举的区别?

using System;

class Example
{
// Define an Enum without FlagsAttribute.
enum SingleHue : short
{
None = 0,
Black = 1,
Red = 2,
Green = 4,
Blue = 8
}; // Define an Enum with FlagsAttribute.
[Flags]
enum MultiHue : short
{
None = 0,
Black = 1,
Red = 2,
Green = 4,
Blue = 8
}; static void Main( )
{
// Display all possible combinations of values.
Console.WriteLine(
"All possible combinations of values without FlagsAttribute:");
for(int val = 0; val <= 16; val++ )
Console.WriteLine( "{0,3} - {1:G}", val, (SingleHue)val); // Display all combinations of values, and invalid values.
Console.WriteLine(
"\nAll possible combinations of values with FlagsAttribute:");
for( int val = 0; val <= 16; val++ )
Console.WriteLine( "{0,3} - {1:G}", val, (MultiHue)val);
}
}
// The example displays the following output:
// All possible combinations of values without FlagsAttribute:
// 0 - None
// 1 - Black
// 2 - Red
// 3 - 3
// 4 - Green
// 5 - 5
// 6 - 6
// 7 - 7
// 8 - Blue
// 9 - 9
// 10 - 10
// 11 - 11
// 12 - 12
// 13 - 13
// 14 - 14
// 15 - 15
// 16 - 16
//
// All possible combinations of values with FlagsAttribute:
// 0 - None
// 1 - Black
// 2 - Red
// 3 - Black, Red
// 4 - Green
// 5 - Black, Green
// 6 - Red, Green
// 7 - Black, Red, Green
// 8 - Blue
// 9 - Black, Blue
// 10 - Red, Blue
// 11 - Black, Red, Blue
// 12 - Green, Blue
// 13 - Black, Green, Blue
// 14 - Red, Green, Blue
// 15 - Black, Red, Green, Blue
// 16 - 16

前面的示例定义了两个与颜色相关的枚举: SingleHueMultiHue 。 后者具有 FlagsAttribute 属性; 前者不具有属性。 此示例显示了在一个整数范围(包括不表示枚举类型的基础值的整数)转换为枚举类型及其显示的字符串表示形式时的行为差异。 例如,请注意,3不能表示为 SingleHue 值,因为3不是任何成员的基础值 SingleHue ,而特性使你可以将 FlagsAttribute 3 表示为 MultiHue 的值 Black, Red

遍历Enum

foreach (var f in Enum.GetValues(typeof(FileSystemRights)))
{
Console.WriteLine(f.ToString().PadLeft(28) + Convert.ToString((int)f, 2).PadLeft(32, '0'));
}
 

接下来是权限的运算:

  1. 权限的加法, 使用与运算来实现。

  0001 | 0100 = 0101, 表示同时具有第一位和第三位的权限管理了, 枚举表示为:

1 Permissions per = Permissions.Insert | Permissions.Update

  2. 权限的减法, 使用与运算+非运算来实现。

  如上面要去掉Insert权限, 操作为:

1 Permissions per &= ~Permissions.Insert
2 // 即是
3 0101 & ~0001 = 0101 & 1110 = 0100

  3. 权限的判断, 使用与运算。

  判断是否有操作权限时, 把用户的的权限与操作权限进行与运算, 如果得到的结果仍是操作权限管理, 则表示用户具有该权限:

1   Permissions per = Permissions.Insert | Permissions.Update;
2   if((per & Permissions.Insert)== Permissions.Insert)
3   {
4   //有操作权限
5   }

  比较过程为 0101 & 0001 = 0001, 0001的0位用与C#位运算把其它位都置成0, 变成只比较1的这一位.

【C#基础概念】枚举 (enum详解)的更多相关文章

  1. Java 枚举(enum) 详解7种常见的用法

    Java 枚举(enum) 详解7种常见的用法 来源 https://blog.csdn.net/qq_27093465/article/details/52180865 JDK1.5引入了新的类型— ...

  2. ELK&ElasticSearch5.1基础概念及配置文件详解【转】

    1. 配置文件 elasticsearch/elasticsearch.yml 主配置文件 elasticsearch/jvm.options jvm参数配置文件 elasticsearch/log4 ...

  3. ELK & ElasticSearch 5.1 基础概念及配置文件详解【转】

    转自:https://blog.csdn.net/zxf_668899/article/details/54582849 配置文件 基本概念 接近实时NRT 集群cluster 索引index 文档d ...

  4. Java 枚举 enum 详解

    本文部分摘自 On Java 8 枚举类型 Java5 中添加了一个 enum 关键字,通过 enum 关键字,我们可以将一组拥有具名的值的有限集合创建为一种新的类型,这些具名的值可以作为常规的程序组 ...

  5. Java 枚举(enum) 详解4种常见的用法

    JDK1.5引入了新的类型——枚举.在 Java 中它虽然算个“小”功能,却给我的开发带来了“大”方便. 大师兄我又加上自己的理解,来帮助各位理解一下. 用法一:常量 在JDK1.5 之前,我们定义常 ...

  6. JAVA中枚举Enum详解

    1.关键字:enum.枚举可以定义成单独的文件,也可以定义在其他类内部. 枚举在类内部的示例: public class EnumInner { public static void main(Str ...

  7. C++之enum枚举量声明、定义、使用与枚举类详解

    C++之enum枚举量声明.定义.使用与枚举类详解 学习一个东西,首先应该指导它能做什么,其次去知道它怎么去做,最后知道为什么去这么做. 知其然知其所以然.不能冒进 ,一步一步的慢慢来.

  8. java枚举使用详解

    在实际编程中,往往存在着这样的“数据集”,它们的数值在程序中是稳定的,而且“数据集”中的元素是有限的. 例如星期一到星期日七个数据元素组成了一周的“数据集”,春夏秋冬四个数据元素组成了四季的“数据集” ...

  9. java枚举使用详解(转)

    在实际编程中,往往存在着这样的"数据集",它们的数值在程序中是稳定的,而且"数据集"中的元素是有限的. 例如星期一到星期日七个数据元素组成了一周的"数 ...

随机推荐

  1. [Anti-AV] 从攻防对抗辩证性分析jsp免杀(一)

    从攻防对抗辩证性分析jsp免杀 从最早的最朴素木马 <%@ page import="java.io.InputStream" %> <%@ page impor ...

  2. Using Swap

    # create swap file dd if=/dev/zero of=/.swap bs=1048576 count=4096 # format swap mkswap /.swap # sta ...

  3. SpringCloud之使用Zookeeper作为注册中心

    SpringCloud之使用Zookeeper作为注册中心 linux安装zookeeper 安装zookeeper 关闭linux防火墙 启动zookeeper 1 创建项目导入依赖和配置文件 &l ...

  4. gin框架简介

    介绍 Gin是一个golang的微框架,封装比较优雅,API友好,源码注释比较明确,具有快速灵活,容错方便等特点 对于golang而言,web框架的依赖要远比Python,Java之类的要小.自身的n ...

  5. golang中的channel

    1. 概念 单纯的将函数并发执行是没有意义的,函数与函数之间需要交换数据才能提现并发执行函数的意义虽然可以使用共享内存来进行数据的交换,但是在共享内存在不同的goroutine中容易发生竟态问题,为了 ...

  6. Linux深入探索01-stty与键盘信号

    ----- 最近更新[2021-12-20]----- 一.简介 最初的 Unix 设定假定人们使用终端连接主机计算机.30多年过去后,情况依然如此,即便是在自己的PC机上运行Unix.多年以来,终端 ...

  7. ORB_SLAM3 + ROS采坑实录(从零开始的毕设生活第一弹)

    ORB-SLAM3配置 https://github.com/UZ-SLAMLab/ORB_SLAM3 https://github.com/shanpenghui/ORB_SLAM3_Fixed E ...

  8. Java 继承01

    继承 ●示例 class Person { public String name; Person(){ System.out.println("Person Constrctor...&qu ...

  9. OPENCV中Line2D对象的意义

    这个对象有四个属性值 VY/VX就是直线的斜率,这俩值代表着直线的归一化向量 X,Y就是直线上的任意一点 直线方程计算如下:

  10. Redis设计与实现

    简述Redis设计与实现 Redis是一个高性能的key-value的非关系型数据库,Redis是运行在内存中的一种数据库,但是它也可以持久化到磁盘中,Redis的实现有着更为复杂的数据结构并且提供对 ...