C#之枚举?枚举!

枚举类型深入解析
本文详细探讨了枚举类型的本质,包括枚举类型与System.Enum之间的关系、枚举类型的值类型特性、枚举成员的获取方式及判断枚举成员的有效性等。此外,还介绍了如何利用位枚举为字体指定多种风格。

关于枚举的种种

BUG吗?虫虫吗?

 

using  System;
using  System.Collections.Generic;
using  System.Text;
using  System.Web;


namespace  枚举值装箱为基类所实现之接口
{
    
enum  AlignMent{Left = 100 ,Right = 1000 ,Center = 10 };
    
class  Program
    {
        
static   void  Main( string [] args)
        {
            
// 著名,此试验在.NET 1.1下无法通过,1.x不支持枚举隐式装箱
            
// 试验目的:枚举值装箱成其基类所实现之接口(隐式装箱)
            
// 第一种:能够允许,说明了枚举值(值类型)能够转换为其基类实现了的接口对象,并且是显式转换
            IConvertible ic  =  (IConvertible)AlignMent.Center;
            
int  i  =  ic.ToInt32( null );
            Console.WriteLine(
" 第一次转换的枚举值Center结果为{0} " , i);
            
            
// 程序暂停
            Console.ReadKey();


            Console.WriteLine();
            IConvertible ic2 
=  (System.Enum)AlignMent.Left;
            i 
=  ic2.ToInt32( null );
            Console.WriteLine(
" 第二次转换(使用的是Enum对象),结果是{0} " , i);
            Console.ReadKey();

            Console.WriteLine();
            ic2 
=  AlignMent.Right;
            i 
=  ic2.ToInt32( null );
            Console.WriteLine(
" 第三次转换,是隐式转换为接口对象,结果是{0} " , i);
            Console.ReadKey();
        }
    }
}

 

枚举类型都是值类型。

System.Enum不是枚举类型,它属于引用类型.System.Enum是一个抽象类(abstract class),所有枚举类型都直接继承自它,当然也同时继承了它的所有成员。

 

Q:既然System.Enum是引用类型,而枚举类型又是直接继承自System.Enum的,那为什么枚举类型却不是引用类型?

A:这种继承关系是隐式的并由编译器负责展开,上面Code #1的Alignment枚举被展开后的IL代码如下:

 

   // Code #02
  . class   public  auto ansi  sealed  Aligment
         extends [mscorlib]System.Enum
ExpandedBlockStart.gif  
{
      .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__
  }

 

 

从声明中,你可以看到Aligment的确是继承自System.Enum的,只是你不能在C#里显式声明这种继承关系。

 

Q:但你好像没有回答为什么枚举类型继承自一个引用类型后,却还是值类型!

A:你知道,所有的值类型都是System.ValueType的后代,枚举类型也不例外,枚举类型直接继承自System.Enum,而System.Enum却又直接继承自System.ValueType的,所以,枚举类型也是System.ValueType的后代。


 

Q:慢着!从System.ValueType派生出来的类型不都应该是值类型吗?为什么System.Enum会是引用类型?

A:正确的说法应该是“值类型都是System.ValueType的后代”,但System.ValueType的后代不全是值类型,System.Enum就是唯一的特例!在System.ValueType的所有后代中,除了System.Enum之外其它都是值类型。事实上,我们可以在.NET的源代码中找到System.Enum的声明:

 

public   abstract   class  Enum : ValueType, IComparable, IFormattable, IConvertible

 

 

 

 

请注意,.NET Framework SDK v2.0.3600.0 Documentation中的Enum声明是错的:

public abstract struct Enum : IComparable, IFormattable, IConvertible

 

 

 


 

Q:开始头晕了,究竟枚举类型、System.Enum、System.ValueType、值类型和引用类型之间存在着什么样的关系?

A:简单的说,

  • 1. 所有枚举类型(enum type)都是值类型。
  • 2. System.Enum和System.ValueType本身是引用类型。
  • 3. 枚举类型(enum type)都是隐式的直接继承自System.Enum,并且这种继承关系只能由编译器自动展开。但System.Enum本身不是枚举类型(enum type)。
  • 4. System.Enum是一个特例,它直接继承自System.ValueType(参见Code #03),但本身却是一个引用类型。

 

             // 目的:看System.enum是不是枚举类型;看它是不是值类型
            Type t  =   typeof (System.Enum);
            
if  (t.IsEnum)
            {
                Console.WriteLine(
" System.Enum是enum type. " );
            } 
            
else
            {
                Console.WriteLine(
" System.Enum<不>是enum type. " ); // 执行这里
            }
            
if  (t.IsValueType)
            {
                Console.WriteLine(
" System.Enum是value type. " );
            } 
            
else
            {
                Console.WriteLine(
" System.Enum<不>是enum type. " ); // 执行这里
            }
            
// 很奇怪,原因全在编译器,System.Enum很特殊.

 

枚举类型可以被装箱成System.Enum、System.ValueType、System.Object或者System.IConvertible、System.IFormattable、System.IComparable。

注意:在.NET 1.1上,枚举类型只能被装箱到System.Enum、System.ValueType、System.Object;而在.NET 2.0上,枚举类型还能被装箱到System.Enum所实现的三个接口:System.IConvertible、System.IComparable、System.IFormattable。对应的装箱操作既可以为隐式的也可以是显式的。

 

 

Q:我们如何获取枚举成员的值,无论成员是否被手动赋值?

A:你可以使用System.Enum的

 

public   static  Array GetValues(Type enumType);

 

 

该方法返回一个包含所有枚举成员的数组:

 

             // 目的:使用System.Enum的GetValues()方法,它会返回所有枚举成员的数组
            AlignMent[] alignments = (AlignMent[])Enum.GetValues( typeof (AlignMent));
            Console.WriteLine(
" 下面列出Alignment枚举类型所有成员: " );
            
foreach  (AlignMent a  in  alignments)
            {
                Console.WriteLine(
" \t{0:G}={0:D} " , a);
            }

 

用System.Enum的

public   static   bool  IsDefined(Type enumType,  object  value);

来判断是否一个整数值能够转换为枚举成员值.

   //  Code #16
  
//  See Code #01 for Alignment.
   static   void  Foo(Alignment a)
ExpandedBlockStart.gifContractedBlock.gif  
{
      
if (!Enum.IsDefined(typeof(Alignment), a))
          
throw new ArgumentException("DO NOT MAKE MISCHIEF!");

      
// Code here
  }

 

 

这样,恶作剧的人将会收到一个警告(异常消息)

 

还可以使用条件判断语句来处理这种情况:

 

   //  Code #17
  
//  See Code #01 for Alignment.
   static   void  Foo(Alignment a)
ExpandedBlockStart.gifContractedBlock.gif  
{
      
if (a != Alignment.Left &&
          a 
!= Alignment.Center &&
          a 
!= Alignment.Right)
          
throw new ArgumentException("DO NOT MAKE MISCHIEF!");

      
// Code here
  }

 

 

或者

 

   //  Code #18
  
//  See Code #01 for Alignment.
   static   void  Foo(Alignment a)
ExpandedBlockStart.gifContractedBlock.gif  
{
      
switch(a)
ExpandedSubBlockStart.gifContractedSubBlock.gif      
{
          
case Alignment.Left:
              Console.WriteLine(
"Cool~");
              
break;
          
case Alignment.Center:
              Console.WriteLine(
"Well~");
              
break;
          
case Alignment.Right:
              Console.WriteLine(
"Good~");
              
break;
          
default:
              Console.WriteLine(
"DO NOT MAKE MISCHIEF!");
              
break;
      }

  }

 

如何为字体同时指定多种风格呢?

A:这个时候你就需要位枚举(Bit Flags),把Code #20修改一下:

   //  Code #21
  
//  I am using the FlagsAttribute to identify a bit flags.
  [Flags]
  
public   enum  FontStyle
ExpandedBlockStart.gifContractedBlock.gif  
{
      Bold        
= 0x0001,
      Italic        
= 0x0002,
      Regular        
= 0x0004,
      Strikethrough    
= 0x0010,
      Underline        
= 0x0020
  }

 

现在,你可以通过按位或运算来为字体指定多种风格了:

   //  Code #22
  
//  See Code #21 for FontStyle.
  Font f  =   new  Font(
        FontFamily.GenericSansSerif,
        
12.0F ,
        FontStyle.Italic 
|  FontStyle.Underline
        );

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值