枚举那些事

枚举是什么类型?

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.");

最后的结果输出12,所以枚举是属于值类型的。

底层类型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-1012,不难发现递增始终为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 = 1Update = 2相加就会等于Save,丢失了唯一的标识

引文

  • 关于枚举的种种 (Enumeration FAQ) [C#, IL, BCL]
  • 枚举为什么使用1,2,4,8,16,32等2… - CSDN博客
  • 枚举类型与位运算

END

  • 如果文章内容能误导大家那真是再好不过了,嘻嘻嘻。
  • 文章内容可能持续变更,修改或添加更多内容,以确保内容的准确性。
  • 文章中大部分观点来自引文的总结,写文章的初衷是为了方便回忆。
  • 更新时间:2018-10-9

你可能感兴趣的:(枚举那些事)