枚举是值类型而System.Enum却是引用类型的原因
2009-09-20 13:37
435 查看
Q:在C#里,我们如何表达枚举类型?
A:你可以使用enum关键字(keyword)来声明一个枚举类型(enum type):
--------------------------------------------------------------------------------
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代码如下:
从声明中,你可以看到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的声明:
请注意,.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),但本身却是一个引用类型。
好吧,现在来看看下面代码,你能猜得出它的输出结果吗?
请别惊讶于程序的运行结果没有任何输出!对于第一个判断,我们很清楚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#代码:
对应的IL代码是:
从IL代码中我们可以看到枚举类型被装箱两次。第一次(L_0003)被装箱成System.Enum,而第二次(L_0014)就被装箱成System.Object。
但如果你让编译器自动为你选择装箱类型的话,它会优先考虑System.Enum:
上面的代码将被编译成如下的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
}
声明:本文转载自http://developer.51cto.com/art/200908/143309.htm
A:你可以使用enum关键字(keyword)来声明一个枚举类型(enum type):
// Code #01 public enum Alignment { Left, Center, Right }
--------------------------------------------------------------------------------
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__ }
从声明中,你可以看到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:开始头晕了,究竟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."); }
请别惊讶于程序的运行结果没有任何输出!对于第一个判断,我们很清楚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); }
对应的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 }
从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); } }
上面的代码将被编译成如下的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
}
声明:本文转载自http://developer.51cto.com/art/200908/143309.htm
相关文章推荐
- 枚举是值类型而System.Enum却是引用类型的原因
- 第十五节 常量const 、 枚举enum 、结构struct 值类型和引用类型
- Java中Enum类型的序列化及类升级不建议用枚举的原因
- Java中Enum类型的序列化及类升级不建议用枚举的原因
- 强化System.Convert.ChangeType, 使其能够转换枚举ENUM和可空类型Nullable
- 序列化类型 System.Data.Entity.DynamicProxies 的对象时检测到循环引用
- enum枚举类型作为成员变量
- System.String是值类型还是引用类型
- java中的枚举类型enum的使用
- enum : C#中的枚举类型,原来有这么多用法
- 使用sqlite数据库时 “System.Data.Common.DbConnection”在未被引用的程序集中定义 错误原因
- C++对C的扩展之枚举类型enum
- CS0012: 类型“System.Data.Objects.DataClasses.EntityObject”在未被引用的程序集中定义。必须添加对程序集 “System.Data.Entity, Version=3.5.0.0, Culture=neutral, PublicKeyToken=
- C语言中枚举(enum)类型的用法
- iOS/object-c: 枚举类型 enum,NS_ENUM,NS_OPTIONS
- 全面解读Java中的枚举类型enum的使用
- 命名空间“System.Web”中不存在类型或命名空间名称“HttpUtility”。是否缺少程序集引用?
- ORM框架中用枚举类型enum来接收数据库中的tinyint字段
- 解决序列化类型为“System.Reflection.RuntimeModule”的对象时检测到循环引用。
- [BUG] CS0234: 命名空间“System.Web.Mvc”中不存在类型或命名空间名称“Ajax”(是否缺少程序集引用?)