枚举是什么类型?
public enum Alignment
{
Left, Center, Right
}
我们先创建一个枚举Alignment
以上Alignment枚举
被展开后的IL代码如下转化为机器语言的中间语言
:
.class public auto ansi sealed Aligment
extends [mscorlib]System.Enum
{
.field public static literal Aligment Left = int32(0x00000000)
.field public static literal Aligment Center = int32(0x00000001)
.field public static literal Aligment Right = int32(0x00000002)
.field public specialname rtspecialname int32 value__
}
从IL代码中可以看出Alignment枚举
继承自System.Enum
。但是System.Enum
是引用类型,可本身继承了ValueType值类型的基类
,体现了Enum的特殊性。
接下来用typeof
进行类型判断。
Type type = typeof(Alingnment);
// 判断
if (type.IsEnum)
Console.WriteLine("I'm enum type.");
if (type.IsValueType)
Console.WriteLine("I'm value type.");
if (type.IsClass)
Console.WriteLine("I'm class type.");
最后的结果输出1
和2
,所以枚举是属于值类型的。
底层类型underlying type
回过头来看看上面的IL代码:
.field public static literal Aligment Center = int32(0x00000001)
其实Center
后面的int32(0x00000001)
就是枚举的底层类型也就是基础类型默认为int32
,并从上至下以0
为开始,递增为1
。
那除了默认的int32
以外,还可以自定义为其他的整数类型
,注意是整数类型包括 byte, sbyte, short, ushort, int, uint, long, ulong
。
public enum DriveType : sbyte
{
CDRom,
Fixed = -2,
Network,
NoRootDirectory = -1,
Ram,
Removable = Network * NoRootDirectory,
Unknown
}
以上未赋予值的,为默认定义值,所以结果如下:
从上至下分别为:0
,-2
,-1
,-1
,0
,1
,2
,不难发现递增始终为1
。
判断枚举值是否存在
System.Enum
提供了IsDefined
这个方法。
我们先来看看这个的方法:
//
// 摘要:
// 返回指定枚举中是否存在具有指定值的常数的指示。
//
// 参数:
// enumType:
// 枚举类型。
//
// value:
// enumType 的常数的值或名称。
//
// 返回结果:
// 如果 enumType 的某个常数具有等于 value 的值,则为 true;否则为 false。
[ComVisible(true)]
public static bool IsDefined(Type enumType, object value);
我们可以看到该方法定义的两个参数,一个是枚举类型另一个是枚举值或名称,下面自定义一个方法:
static void Foo(Alignment a)
{
if (!Enum.IsDefined(typeof(Alignment), a))
throw new ArgumentException("DO NOT MAKE MISCHIEF!");
else
Console.WriteLine(a.ToString());
}
这样如果参数a
不在枚举值内,则会抛异常。如果存在那就执行你想要执行的操作。
位标识Bit Flags
位标识就是给枚举加上flags
特性,如:
[Flags]
public enum Options
{
None=0,
Insert = 1, //二进制: 0001
Update = 2, //二进制: 0010
Save = 4, //二进制: 0100
Delete = 8, //二进制: 1000
Query = 16 //二进制:10000
}
先看看flags
这个特性:
//
// 摘要:
// 指示可以将枚举作为位域(即一组标志)处理。
[AttributeUsage(AttributeTargets.Enum, Inherited = false)]
[ComVisible(true)]
public class FlagsAttribute : Attribute
{
//
// 摘要:
// 初始化 System.FlagsAttribute 类的新实例。
public FlagsAttribute();
}
摘要:指示可以将枚举作为位域
,标志为位域后就可以进行位运算,将枚举值进行多个拼接,位运算是针对二进制位进行的运算,常用的运算主要有与&
,或|
,非~
。
以上面的Option枚举来个例子:
Options hasOps = Options.Insert | Options.Update;
if ((hasOps & Options.Insert) == Options.Insert)
{
Console.WriteLine("Has {0}", Options.Insert);
}
来看下if
里面发生了什么:
- 首先
hasOps = Options.Insert | Options.Update
=0001|0010
,因为|
运算符:1 | 0 = 1
,可得结果为:0011
。 - 然后括号内
hasOps & Options.Insert
=0011 & 0001 = 0001
。 - 最后与 == 右边的
Options.Insert = 0001
,对比相等返回True。
最后提一点,位枚举的赋值可以看出来是2的指数倍。
其目的就是位运算时结果不会出现复合值,拿Save = 4
来说,默认情况下Save = 3
,那Insert = 1
和Update = 2
相加就会等于Save
,丢失了唯一的标识
。
引文
- 关于枚举的种种 (Enumeration FAQ) [C#, IL, BCL]
- 枚举值为什么使用1,2,4,8,16,32等2… - CSDN博客
- 枚举类型与位运算
END
- 如果文章内容能误导大家那真是再好不过了,嘻嘻嘻。
- 文章内容可能持续变更,修改或添加更多内容,以确保内容的准确性。
- 文章中大部分观点来自引文的总结,写文章的初衷是为了方便回忆。
- 更新时间:2018-10-9