Enumeration提供了一些非常炫酷的功能,相信大多数开发人员都不熟悉。而这些新功能极大的简化了应用程序的开发。
15.1枚举类型
枚举类型(enumerated types)定义了一组“符号名称/值”配对。
以下Color类型定义了一组符号,每个符号都标识一种颜色:
internal enum Color{ White,//赋值0 Red, //赋值1 Greed,//赋值2 Blue, //赋值3 Orange//赋值4 }
当然,我们也可以写个程序用0代表白色,1代表红色,以此类推。
但不应该将这些数字硬编码到代码中,而应该换用枚举类型,因为:
- 枚举类型使程序更容易编写、阅读和维护。在代码中使用符号名称代替数字是程序设计的一贯主张。
- 枚举类型是强类型的,便于类型检验。
每 个枚举类型都直接从System.Enum派生,后者从System.ValueType派生。而System.ValueType又从 System.Object派生。所以,枚举类型是值类型,可表示成未装箱和已装箱形式。有别于其他的值类型,枚举类型不能定义任何方法、属性和事件。
编译枚举类型时,C#编译器会把每个符号转换成类型的一个常量字段。例如,编译器会把前面的Color枚举类型看成以下代码:
internal struct Color : System.Enum{ //以下是一些公共常量,它们定义了Color的符号和值 public const Color While = (Color)0; public const Color Red = (Color)1; public const Color Green = (Color)2; public const Color Bule = (Color)3; public const Color Orange = (Color)4; //以下是一个公共实例字段,它包含一个Color变量的值, //不能写代码来直接引用这个实例字段 public Int32 value__; }
但C#编译器实际上并不编译上面这段代码,因为它禁止定义从System.Enum这一特殊类型派生的类型。
枚举类型定义的符号是常量值,所以当C#编译器一旦发现代码中引用了一个枚举类型的符号,就会在编译时用数值替代符号,代码将不再引用定义了符号的枚举类型。
简单地来说,枚举类型只是一个结构,其中定义了一组常量字段和一个实例字段。常量字段会嵌入到程序集的元数据中,并可通过反射来访问。这意味着在运行时,获得了与一个枚举类型关联的所有符号及其值。还意味着我们可以将一个字符串符号转换成其对应的数值。
下面来讨论System.Enum类型中的一些主要方法:
GetUnderlyingType:
public static Type GetUnderlyingType(Type enumType);
该方法返回用于容纳一个枚举类型的值的基础类型。每个枚举类型都有一个基础类型,可以是byte,short,int(最常用,也是C#默认选择的),long等。
注意:
- C#要求只能指定基元类型名称,如果使用FCL类型名称(比如Int32),会报错。
- 我们定义的枚举类型应该与需要调用它的那个类型同级,可减少代码录入的工作量。
以下代码演示了如何声明一个基础类型为byte的枚举类型:
internal enum Color : byte{ White, Red, Greed, Blue, Orange}static void Main() { Console.WriteLine(Enum.GetUnderlyingType(typeof(Color))); //System.Byte }
C#中typeof() 和 GetType()区别:
- typeof(x)中的x,必须是具体的类名、类型名称等,不可以是变量名称。
- GetType()方法继承自Object,所以C#中任何对象都具有GetType()方法,它的作用和typeof()相同,返回Type类型的当前对象的类型。
C#编译器将枚举类型视为基元类型,所以,我们可以使用许多熟悉的操作符(==,!=,<,>,<=,>=,+,-,^,&,|,++,--)来操纵枚举类型的实例。所有这些操作符实际作用于每个枚举类型实例内部的value_实例字段。
ToString:
public string ToString(string format);
给定一个枚举类型的实例,我们可以调用从System.Enum继承的ToString方法。
public static class Program { static void Main() { Color c = Color.Blue; Console.WriteLine(c.ToString());//"Blue" 常规格式 Console.WriteLine(c.ToString("G"));//"Blue" 常规格式 Console.WriteLine(c.ToString("D"));//"3" 十进制格式 Console.WriteLine(c.ToString("X"));//"03" 十六进制格式 } } internal enum Color : byte { White, Red, Greed, Blue, Orange }
Format:
public static string Format(Type enumType, object value, string format);
可调用它格式化一个枚举类型的值。
Console.WriteLine(Enum.Format(typeof(Color), 3, "G"));//显示"Blue"
GetValues:
public static Array GetValues(Type enumType);
获取枚举类型中定义的所有符号以及对应的值。
数组的声明:int[] myIntArray; 注:声明数组时,方括号 ([]) 必须跟在类型后面,而不是变量名后面。
Color[] colors = (Color[])Enum.GetValues(typeof(Color));Console.WriteLine("Number of symbols defined:" + colors.Length); Console.WriteLine("Value\tSymbol\n-----\t------"); foreach (Color c in colors) { Console.WriteLine("{0,5:D}\t{0:G}", c); }
GetName:
public static string GetName(Type enumType, object value);
返回数值的字符串表示。
Enum.GetName(typeof(Color), 3);//"Blue"
GetNames:
public static string[] GetNames(Type enumType);
返回一个符号名称的String数组。
Enum.GetNames(typeof(Color));//{string[5]}//[0]: "White" //[1]: "Red" //[2]: "Greed" //[3]: "Blue" //[4]: "Orange"
Parse:
public static object Parse(Type enumType, string value, bool ignoreCase)
将一个符号转换成枚举类型的实例。
Color c = (Color)Enum.Parse(typeof(Color), "orange", true); //Orange Enum.Parstypeof(Color), "0", true);//White
TryParse:
public static bool TryParse(string value, bool ignoreCase, out TEnum result) where TEnum : struct;
将一个或多个枚举常数的名称或数字值的字符串表示转换成等效的枚举对象。用于指示转换是否成功的返回值。
TryParse方法类似于Parse方法,不同之处在于TryParse方法在转换失败时不引发异常。
传递到out形参的实参可以不先经过初始化,就能传递。(ref/out区别)
Color c=new Color(); bool a=Enum.TryParse("Brown", false, out c);//false, 枚举中没有定义Brown
IsDefine:
public static bool IsDefined(Type enumType, object value);
判断一个值对于一个枚举类型是否合法。
Enum.IsDefined(typeof(Color), "white");//false, 执行的是区分大小写的检查 Enum.IsDefined(typeof(Color), 5);//false, Color枚举类型没有与5对应的符号
15.2位标志
我们可以将位标志当做一种特殊的枚举类型。
FileAttributes类型是基本类型为Int32的枚举类型,其中每一位都反映文件的一项属性。
[Flags]//指示可以将枚举作为位域(即一组标志)处理 public enum FileAttributes { ReadOnly = 1, Hidden = 2, System = 4, Directory = 16, Archive = 32, Device = 64, Normal = 128, Temporary = 256, SparseFile = 512, ReparsePoint = 1024, Compressed = 2048, Offline = 4096, NotContentIndexed = 8192, Encrypted = 16384, IntegrityStream = 32768, NoScrubData = 131072 }
以上FileAttributes类型中,1的二进制为1,2的二进制为10,4的二进制为100。也就是说可以用每个二进制位来确认唯一性,这就是位标志的原理。
public static void Main() { //得到可执行文件(.exe文件)的相对路径(如:"...\bin\Debug\ConsoleApplication1.exe") String file = Assembly.GetEntryAssembly().Location; //调用System.IO.File类型的GetAttributes方法,会返回FileAttributes类型的一个实例 FileAttributes attributes = File.GetAttributes(file); //判断文件是否隐藏,因为二进制1&1才为1,所以只要存在最后的数值不为1,&的结果就为0 Console.WriteLine("IS {0} hidden?{1}", file, (attributes & FileAttributes.Hidden) != 0); //判断文件是否隐藏,换种写法。Enum有一个HasFlag方法,确定当前实例attributes中是否设置了一个或多个位域 Console.WriteLine("IS {0} hidden?{1}", file, attributes.HasFlag(FileAttributes.Hidden)); //将一个文件的属性改为只读和隐藏 File.SetAttributes(file, FileAttributes.ReadOnly | FileAttributes.Hidden); }