枚举是值类型而System.Enum却是引用类型的原因

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

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

  1. // Code #01
  2. public enum Alignment
  3. {
  4. Left,
  5. Center,
  6. Right
  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代码如下:

  1. // Code #02
  2. .class public auto ansi sealed Aligment
  3. extends [mscorlib]System.Enum
  4. {
  5. .field public static literal Aligment Left = int32(0x00000000)
  6. .field public static literal Aligment Center = int32(0x00000001)
  7. .field public static literal Aligment Right = int32(0x00000002)
  8. .field public specialname rtspecialname int32 value__
  9. }

从声明中,你可以看到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的声明:

  1. 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),但本身却是一个引用类型。
好吧,现在来看看下面代码,你能猜得出它的输出结果吗?

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

请别惊讶于程序的运行结果没有任何输出!对于第一个判断,我们很清楚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#代码:

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

对应的IL代码是:

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

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

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

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

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

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

转载于:https://www.cnblogs.com/jayden-en/p/5131733.html

C#一探究竟——枚举相关推荐

  1. 中文NLP的分词真有必要吗?李纪为团队四项任务评测一探究竟 | ACL 2019

    作者| Yuxian Meng.Xiaoya Li.Xiaofei Sun.Qinghong Han.Arianna Yuan. Jiwei Li 译者 | Rachel 责编 | Jane 出品 | ...

  2. bagging算法_Bagging/Boosting傻傻分不清?来一探究竟吧~

    你是否还在迷惑什么是Bagging?你是否还在纠结Bagging和Boosting的区别到底在哪??你是否还在探索Bagging的具体用法???那就一起来看看吧! Bagging 同一个学习算法在来自 ...

  3. 带你一探究竟财务报表软件!

    随着数字化时代的到来,数据生产成倍增长,对于一个公司而言,财务数据的分析显得异常重要,而仅仅只会简单的做账是远远不够的.从长远来看,学会财务报表分析,站在管理的角度为企业提供财务方案,最大程度地为企业 ...

  4. 青铜5:一探究竟-从synchronized理解对象头中的锁

    在前面的文章<青铜4:synchronized用法初体验>中,我们已经提到锁的概念,并指出synchronized是锁机制的一种实现.可是,这么说未免太过抽象,你可能无法直观地理解锁究竟是 ...

  5. Android Activity生命周期一探究竟 及 主Activity里带Thread跳转时的注意事项

    问题背景: 最近用到了主Activity里有一个Thread在处理数据,当跳转到第二个Activity时,希望Thread暂停.从第二个Activity回来时这个Thread再接着运行.今天得空顺着这 ...

  6. .NET中的枚举(Enum)

    摘要:.NET中的枚举分为简单枚举和标志枚举,这次主要总结一下标志枚举适用条件,以及它的使用方法,并在文章的最后列举枚举使用的一些规范. 在刚接触.NET的枚举时,只用简单的枚举,对于标记枚举,只知道 ...

  7. 满满干货!!详细的平台分析,原来发布文章竟有如此多的学问?!不看不知道,一看吓一跳啊!

    ** 如何在媒体上发布文章总结报告 ## 一.同类三种发布平台 1.小红书 小红书是一个生活方式平台和消费决策入口 ,创始人为毛文超和瞿芳.截至2019年7月,小红书用户数已超过3亿 ;截至到2019 ...

  8. 芯片手册中的英语言学——寄存器缩写命名方式探究

    今日在暴啃芯片手册!其中最令人头疼的就是成百上千的寄存器英文缩写. 在实际学习过程中会发现,很多寄存器会加上三个四个甚至更多的英文缩写字母作为后缀,而其很多时候百度并不一定能查询到全拼!这使得我们在学 ...

  9. 探究推荐引擎瞬间被“秒”背后:究竟是什么让用户接踵而至?

    摘要: 推荐引擎究竟是怎样的一款产品?用户接踵而至的背后,说明了怎样的一个现象?这款产品成功的背后,又有哪些不为人知的艰辛? 6月16日,阿里云技术专家郑重(卢梭)将做客云栖社区,直播分享<技术 ...

最新文章

  1. 广东java工资一般多少_广东java工资待遇,广东java工资一般多少,广东java工资底薪最低多少...
  2. 计算机视觉与图像处理相关的国内外重要期刊汇总
  3. Selenium之定位元素常用的8种方法整理(第一篇)
  4. Lesson 4.6 逻辑回归的手动实现
  5. 在linux下安装db2全过程
  6. GridView的 使用
  7. 【读书笔记】《Javascript语言精粹》
  8. C# 一次性获取二维数组中的一维数组数据
  9. CISCO路由器的备份与还原(2)
  10. python github 12306 文贤平_GitHub排名第一!免费最强“抢票神器”在手,程序员抢票再不用跪求加速包...
  11. 萤火虫小程序_成都以北2h的森林小镇!养在深闺的萤火虫、穿行山间的火车,在这露营解锁夏天的小秘密...
  12. 百度地图加载shp_ArcGIS中加载无偏移谷歌卫星影像!奥维官方插件与ArcGIS协同互动...
  13. freebsd emacs_golang快速入门[6.2]-集成开发环境-emacs详解
  14. 为echarts的叠加柱状图设置颜色
  15. 什么是张量(tensor)?
  16. [Tensorflow]关于TFRecord和tf.Example的使用
  17. 对P5基本二维图像绘制库的交互性扩展
  18. 水逆的京东,2019还能翻盘吗?
  19. 贵州大学计算机考研资料汇总
  20. 解析LDO的基本原理与主要参数

热门文章

  1. 在linux中添加字体
  2. 【iOS】利用CocoaPods创建私有库进行组件化开发
  3. web安全之SQL注入---第四章 如何进行SQL注入攻击
  4. 常用品牌交换机镜像抓包配置
  5. lvs+keepalived配置
  6. 使用asp.net改变图片颜色
  7. 自定义jQuery插件
  8. Java编写的测试加法计算的工具
  9. MySQL基本配置命令总结
  10. 什么时候对象会进入老年代?