有关C#枚举的问答集锦:基础篇

开发 后端
本文汇总了一些有关C#枚举的问答。本文是第一部分,截取了基础方面的问答十则。

Q:在C#里,我们如何表达枚举类型?

A:你可以使用enum关键字(keyword)来声明一个枚举类型(enum type):

// Code #01  
public enum Alignment  
{  
    Left,  
    Center,  
    Right  
}  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

--------------------------------------------------------------------------------

Q:C#枚举类型是值类型(value type)还是引用类型(reference type)?

A:枚举类型都是值类型。

--------------------------------------------------------------------------------

Q:System.Enum是枚举类型么?

A:不是。

--------------------------------------------------------------------------------

Q:System.Enum与枚举类型(enum type)有什么关系?

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

--------------------------------------------------------------------------------

Q:那么System.Enum属于引用类型啦?

A:是的。

--------------------------------------------------------------------------------

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

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

// Code #02  
.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__  
}  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

从声明中,你可以看到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  
  • 1.

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

public abstract struct Enum : IComparable, IFormattable, IConvertible

--------------------------------------------------------------------------------

Q:开始头晕了,究竟C#枚举类型、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),但本身却是一个引用类型。
好吧,现在来看看下面代码,你能猜得出它的输出结果吗?

// Code #04  
static void Main()  
{  
    Type t = typeof(System.Enum);  
 
    if (t.IsEnum)  
        Console.WriteLine("I'm enum type.");  
 
    if (t.IsValueType)  
        Console.WriteLine("I'm value type.");  
}  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

请别惊讶于程序的运行结果没有任何输出!对于***个判断,我们很清楚System.Enum并不是枚举类型。但第二个判断呢?System.Enum明明继承自System.ValueType,却不承认是System.ValueType的后代!这是.NET上的一个特例,恰恰体现出System.Enum是特殊性。

--------------------------------------------------------------------------------

Q:既然枚举类型是值类型,自然会涉及到装箱和拆箱(boxing and unboxing)的问题,那么枚举类型会被装箱成什么呢?[Updated]

A:枚举类型可以被装箱成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。对应的装箱操作既可以为隐式的也可以是显式的。

下面的C#代码:

// Code #05  
// See Code #01 for Alignment.  
static void Main()  
{  
    Alignment a = Alignment.Center;  
 
    Console.WriteLine(a.ToString());  
 
    Console.WriteLine(a);  
}  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

对应的IL代码是:

// Code #06  
.method private hidebysig static void Main() cil managed  
{  
    .entrypoint  
    // Code Size: 32 byte(s)  
    .maxstack 1  
    .locals (  
          EnumerationFaq.Alignment alignment1)  
    L_0000: ldc.i4.1   
    L_0001: stloc.0   
    L_0002: ldloc.0   
    L_0003: box EnumerationFaq.Alignment  
    L_0008: call instance string [mscorlib]System.Enum::ToString()  
    L_000d: call void [mscorlib]System.Console::WriteLine(string)  
    L_0012: nop   
    L_0013: ldloc.0   
    L_0014: box EnumerationFaq.Alignment  
    L_0019: call void [mscorlib]System.Console::WriteLine(object)  
    L_001e: nop   
    L_001f: ret   
}  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

从IL代码中我们可以看到枚举类型被装箱两次。***次(L_0003)被装箱成System.Enum,而第二次(L_0014)就被装箱成System.Object。

但如果你让编译器自动为你选择装箱类型的话,它会优先考虑System.Enum:

// Code #07  
// See Code #01 for Alignment.  
class Program  
{  
    static void Main()  
    {  
        Alignment a = Alignment.Center;  
 
        Print(a);  
    }  
 
    static void Print(IConvertible c)  
    {  
        Console.WriteLine(c);  
    }  
 
    static void Print(IFormattable f)  
    {  
        Console.WriteLine(f);  
    }  
 
    static void Print(IComparable c)  
    {  
        Console.WriteLine(c);  
    }  
 
    static void Print(Object o)  
    {  
        Console.WriteLine(o);  
    }  
 
    static void Print(ValueType v)  
    {  
        Console.WriteLine(v);  
    }  
 
    static void Print(Enum e)  
    {  
        Console.WriteLine(e);  
    }  
}  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.

上面的代码将被编译成如下的IL:

// Code #08  
.method private hidebysig static void Main(string[] args) cil managed  
{  
    .entrypoint  
    // Code Size: 15 byte(s)  
    .maxstack 1  
    .locals (  
          EnumerationFaq.Alignment alignment1)  
    L_0000: ldc.i4.1   
    L_0001: stloc.0   
    L_0002: ldloc.0   
    L_0003: box EnumerationFaq.Alignment  
    // 调用static void Print(Enum e);  
    L_0008: call void EnumerationFaq.Program::Print([mscorlib]System.Enum)  
    L_000d: nop   
    L_000e: ret   
}  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

--------------------------------------------------------------------------------

以上就是有关C#枚举类型的一些问答,更多问答待续。

【编辑推荐】

  1. C#枚举和数学习经验总结
  2. 浅谈如何利用C#枚举所有的窗体
  3. C#记忆功能的地址栏控件
  4. 描述C#调用外部进程
  5. C#语言操纵数据库事务
责任编辑:yangsai 来源: CSDN博客
相关推荐

2009-08-11 14:55:44

C#枚举

2009-08-11 15:24:03

C#枚举

2010-08-12 18:01:38

ibmdwJazz

2010-12-29 09:51:06

配置vsftpdDebian

2012-07-05 09:42:08

jQuery

2010-03-12 09:47:22

2009-08-18 13:00:59

C#枚举类型

2009-08-18 10:30:30

C#枚举

2011-03-03 13:25:57

2010-06-02 13:30:10

IPv6标准

2009-08-18 11:07:06

C#枚举类型

2010-01-18 16:14:43

配置VLAN交换机

2009-12-29 14:41:12

ADSL常见硬件问题

2009-08-18 13:35:06

C#枚举文件

2009-08-18 09:37:14

C#枚举类型

2009-08-17 18:31:39

C# 枚举

2009-08-18 12:52:33

C#枚举类型

2009-08-18 10:35:46

C#枚举类型

2009-08-17 17:56:32

C# 枚举

2009-09-01 17:08:35

C# Color枚举
点赞
收藏

51CTO技术栈公众号