枚举类型和位标志

1. 枚举类型                                                                                       
2. 位标志
3. 为枚举类型添加方法

枚举类型

定义了一组“符号名称/值”配对
例如:Color类型的一组符号,每一种代表一种颜色,以下没有特殊说明都用此枚举类型
internal enum Color{
    White,    //赋值0
    Red,      //1
    Green,    //2    
    Blue,     //3
    Orange    //4    
}
 
为什么使用枚举类型?
1.枚举类型使程序更易编写、阅读、和维护。
2.枚举是强类型的,例如,试图将Color.Orange作为一个值传递给要求Fruit枚举类型作为参数的方法,编译器会报错
意思是说Fruit枚举类型应该是水果,而Color枚举定义的是颜色,虽然两个枚举都有一个Orange,但分别代表的是橙子和橙色。
 

 
public static Type GetUnderlyingType(Type enumType);    //System.Enum中定义
public Type GetEnumUnderlyingType();                    //System.Type中定义
 
这些方法用来返回用于容纳一个枚举类型的值的基础类型。
 
每个枚举都有一个基础类型,它可以是byte、sbyte、short、ushort、int(最常用的,也是C#默认选择的)、uint、long或unlong
 
虽然这些C#基元类型都有对应的FCL类型(注:不要混淆“基础类型”和“基元类型”)
 
但C#编译器为了简化本身的实现,要求只能指定基元类型名称,如果使用FCL类型名称(比如Int32),会报错。
 
例如:声明一个基础类型为byte的枚举类型
internal enum Color:byte{
    White,   
    Red,      
    Green,      
    Blue,     
    Orange    
}
 
Console.WriteLine(Enum.GetUnderlyingType(typeof(Color)));
 
输出:System.Byte
 

可以调用System.Enum的静态方法GetValues或者System.Type的实例方法GetEnumValues获取一个数组,该数组的每个元素都对应枚举类型中的一个符号名称,
 
每个元素都包含符号名称的数值。
 
public static Array GetValues(Type enumType);    //System.Enum
public Array GetEnumValues();                    //System.Type
 
利用ToString方法使用,可以显示枚举类型中的所有符号名称及对应的数值。如下所示:
 
            Color[] color =(Color[]) Enum.GetValues(typeof(Color));
            Console.WriteLine("Value\tSymbols\n-----\t-------");
            foreach (Color cc in color)
            {
                //以十进制和常规格式显示每个符号
                Console.WriteLine("{0,5:D}\t{0:G}",cc);
            }
            /*输出:*
             * Value   Symbols
                -----   -------
                    0   White
                    1   Red
                    2   Green
                    3   Blue
                    4   Orange
             * 
             */
 
除了GetValues方法,System.Enum和System.Type类型还提供了以下方法来返回枚举的符号:
 
//返回数值的字符串表示
 
public static String GetName(Type enumType,Object value);    //System.Enum中定义
public String GetEnumName(Object value);                     //Syste.Type 中定义
 
//返回一个String数组。枚举每个符号都对应一个String
 
public static String[] GetNames(Type enumType);              //System.Enum中定义
public String[] GetEnumNames();                              //Syste.Type 中定义
 
//通过符号查找对应的值
 
//enumType:枚举类型、value:符号、ignoreCase:是否忽略大小写
public static Object Parse(Type enumType,String value);
public static Object Parse(Type enumType,String value,Boolean ignoreCase);
 
public static Boolean TryParse<TEnum>(String value,out TEnum result) where TEnum:struct
public static Boolean TryParse<TEnum>(String value,Boolean ignoreCase, out TEnum result) where TEnum:struct
 
如何使用上面这些方法:
 
Color c1 = (Color)Enum.Parse(typeof(Color), "red", true);
// c1 = (Color)Enum.Parse(typeof(Color), "red222", false);//因为没有定义red222所以抛出ArgumentException异常
Enum.TryParse<Color>("1", false,out c1);
Enum.TryParse<Color>("23", false, out c1);
 
IsDefined方法判断一个数值对于一个枚举类型是否合法:
 
public static Boolean IsDefined(Type enumType,Object value);      //System.Enum中定义
public Boolean IsEnumDefined(Object value);                       //Syste.Type 中定义
 
//True,因为Color将Red定义为1
Console.WriteLine(Enum.IsDefined(typeof(Color),1));
//True,因为Color将White定义为0
Console.WriteLine(Enum.IsDefined(typeof(Color), "White"));
//False,区分大小写
Console.WriteLine(Enum.IsDefined(typeof(Color), "white"));
 
IsDefine方法被经常用于参数校验,例如:
public void SetColor(Color c){
    if(!Enum.IsDefined(typeof(Color),c)){
        throw new ArgumentOutOfRangeException("c")
    }
}
参数校验是非常有用的功能,因为其他人可能向下面这样调用SetColor:
 
SetColor((Color),547);
 
没有和值547对应的符号,所以抛出一个异常,注意IsDefined它是区分大小写的,而且它是通过反射原理,性能下降
 

位标志
 
位操作符:&和|
 
FileAttributes类型在FCL中的定义:
[Flags, Serializable]
    public enum FileAttributes { 
        ReadOlny    =0x0001,
        Hidden      =0x0002,
        System      =0x0004,
        Directory   =0x0010,
        Archive     =0x0020,
        Device      =0x0040,
        Normal      =0x0080,
        Temporary   =0x0100,
        SparseFile  =0x0200
        //.....
    }
 
String file = Assembly.GetEntryAssembly().Location;
            FileAttributes attribute = File.GetAttributes(file);
            Console.WriteLine("Is {0} hidden ? {1}",file,(attribute&FileAttributes.Hidden)!=0);
//将一个文件属性改为只读和隐藏
File.SetAttributes(file, FileAttributes.ReadOnly | FileAttributes.Hidden);
 
正如FileAttributes类型展示的那样,经常都要用枚举类型来表示一组可以组合的位标识。不过,虽然枚举类型和位标识相似,但它们的语义不尽相同。
 
例如,枚举类型表示单个数值,而位标识表示一组位,其中一些位处于on状态,有些位处于off状态。
 
定义用于标识位标志的枚举类型时,当然应该显示为每一个符号分配一个数值,通常,每个符号都有单独的一个位处于on状态,此外,经常都要定义一个值为
 
0的None符号。还可以定义一些代表用位组合的符号,另外强烈建议向枚举类型应用System.FlagsAttributes这个定制的attribute类型
 
[Flags]
internal enum Actions{
    None=0,
    Read=0x0001,
    Write=0x0002,
    ReadWrite=Actions.Read|Actions.Write,
    Delete =0x0004,
    Query=0x0008,
    Sync=0x0010
}
 
因为Actions是枚举类型,所以在操作位标志枚举类型时,例如:
 
Actions actions=Actions.Read|Actions.Delete;    //0x0005
Console.WriteLine(actions.ToString());          //Read,Delete
 
在调用ToString时,它会试图将数值转为对应的符号。现在的数值是0x0005没有对应的符号,不过ToString 方法检测到Actions类型上存在[Flags]这个attribute,所以ToString方法不会将该数值视为单独的值,相反,会把它视为一组位标识。由于0x0005由0x0004和0x0001组合而成,所以ToString会生成字符串Read,Delete,如果没有Flags,ToString会返回5
 

向枚举类型添加方法
 
internal static class FileAttributesExtensionMethods{
    //模拟一个包含扩展方法的静态类
}
posted @ 2012-11-30 14:40  Lordbaby  阅读(634)  评论(0)    收藏  举报