C#基础之枚举
1.认识Enum
以前一直以为Enum是值类型,在VS中查看Enum的定义时才发现它是一个抽象的类。但是这个类很奇怪,Enum继承了ValueType这个很熟悉的值类型基类,它是唯一一个继承自ValueType类型但又不是值类型的引用类型。Enum还实现了3个接口,分别是IComparable、IFormattable、IConvertible。IComparable中只有一个CompareTo方法用来进行比较,IFormattable中也只有一个方法ToString用来格式化当前枚举对象,IConvertible则是用来进行值类型转化为引用类型的接口。我们自定义的枚举正是继承于Enum类型,之所以要继承Enum,是因为在枚举中不能定义任何属性、方法或事件(其实也就是方法),可是我们还需要操作枚举啊,这时就会用到Enum中的方法。不过又有一个奇怪的事情,在vs中随便写一段枚举如下面代码所示。查看这段代码的IL指令时发现hh的开头部分是.class,我脑子里大概记得枚举是值类型可是在IL中确实显示的是.class。接着我随便写了一个struct放到IL中发现仍然是以.class开头。然后我才发现自己有一个误区,值类型不是struct,struct是属于值类型的。当然最大的疑惑还是.class,从这里也可以看出在.net中所有类型都是class。不要忘了值类型也是继承于ValueType引用类型,到这里问题又来了,值类型是不能继承引用类型的,只能实现接口,那系统内这些值类型继承引用类型到底是如何实现的,特别是内存的分配。
查阅资料后有一位前辈的意思是系统分配内存时会看这个类型是否继承ValueType,如果继承呢就是值类型,否则就是引用类型。不过刚学习了Enum显然这个观点并不完全对。但是思想可以理解那就是值类型继承引用类型时,值类型会在栈上开辟空间,引用类型会在堆上开辟空间,然后值类型中会有一个指针指向了堆中的引用类型。再回到IL中,我仔细比较了枚举类型、结构体、类这三种类型的IL头部,发现还是有区别的。如下面第二段IL指令所示,它们的区别在于auto与sequential,在.net中有一个内存布局字段LayoutKind,它用来修饰程序中的类型。LayoutKind有三种结果,auto表示内存布局有.net自动分配,sequential表示安照次序相继的分配,explicit我查英文是明确的意思,具体在什么地方用我还不清楚。类和枚举是auto自动分配的,struct和Int32值类型是sequential方式分配的,这个结果显示枚举是引用类型而不是值类型。对这个结果我感到很奇怪,在后面认识枚举中我将写的代码弄得Reflector中发现确实产生了装箱,这说明枚举确实是值类型。这个地方我没搞明白,还请各位前辈指点迷津!
public enum hh
{
a,
b,
c,
} .class public auto ansi sealed hh
extends [mscorlib]System.Enum
{
.field public static literal valuetype Microsoft.hh a = int32() .field public static literal valuetype Microsoft.hh b = int32() .field public static literal valuetype Microsoft.hh c = int32() .field public specialname rtspecialname int32 value__ }
//枚举
.class public auto ansi sealed hh extends [mscorlib]System.Enum //类
.class public auto ansi beforefieldinit MyClass extends [mscorlib]System.Object //结构体
.class public sequential ansi sealed beforefieldinit jj extends [mscorlib]System.ValueType //Int32
.class public sequential ansi serializable sealed beforefieldinit Int32 extends System.ValueType
2.使用枚举
回到枚举,枚举默认就是int类型的,它里面只能声明byte,sbyte,short,ushort,int,uint,long ulong这8种类型,在代码里写a='2'是不会报错的,在IL中已经转换为2的asci码50了。关于枚举中的成员赋值,总结起来有3点要注意。第一如果不为枚举成员指定初始值,其将会从0开始自动增加就如上面第一段代码中的枚举hh;第二如果在枚举中为枚举成员指定了初始值,其余剩下的成员将依旧以指定的成员初始值开始依次增加,如下面代码段所示c=6而f=2。第三当在外部写hh h=new hh()时,此时h并不是指第一个枚举值2,而是为0,也就是指向枚举成员中值为0的枚举成员。枚举里面只能声明上面提到的8种整型,因此枚举可以与整型进行相互转换,但是要注意都必须显示转换。如下面第二段代码所示,还有一些关于枚举的简单使用也在其中。最后还有一个关于枚举的功能就是位枚举,其实我们也经常看到。比如一个颜色枚举,有时候我们需要指定多个颜色就像Color.Red|Color.Blue|Color.Green这样。具体代码如下面第三段代码所示,在使用水果枚举时我们可以指定多个值,这给我们带来了不少的好处。只不过判断这种枚举组合是否是一个正确的组合不好判断。
使用枚举可以让代码变得清晰简单,但是它的功能也可以使用类的静态字段来实现。不过枚举中的值将在编译时即替换为常量,而类的字段则不会,再加上类是分配在堆上的大型资源,性能肯定没有枚举快。同样结构体也可以声明公共字段来完成枚举的功能,那枚举和只含字段的结构体相比哪个更好呢?我觉得还是枚举,因为枚举还有一个优点,前面我特别强调了枚举与整型进行转换时一定要强制转换。这使得强类型的枚举比结构体更加安全,比如有一个方法MyFunc(MyEnum myEnum),参数中指定为枚举类型的话那么传参时将只能传入枚举类型,否则会报错。如果是结构体声明的int类型将有可能使这个方法被传入错误的参数而导致系统不稳定。所以以后对于需要声明分类信息的功能,枚举是首选。我资历浅,如有错误还请各位前辈指出。
public enum hh
{
a=,
b=,
c,
d,
e=,
f,
g,
h
} .class public auto ansi sealed hh
extends [mscorlib]System.Enum
{
.field public static literal valuetype Microsoft.hh a = int32() .field public static literal valuetype Microsoft.hh b = int32() .field public static literal valuetype Microsoft.hh c = int32() .field public static literal valuetype Microsoft.hh d = int32() .field public static literal valuetype Microsoft.hh e = int32() .field public static literal valuetype Microsoft.hh f = int32() .field public static literal valuetype Microsoft.hh g = int32() .field public static literal valuetype Microsoft.hh h = int32() .field public specialname rtspecialname int32 value__ }
static void Main(string[] args)
{
hh h1 = new hh();
Console.WriteLine(h1.ToString());//输出0
hh h2 = hh.a;
Console.WriteLine(h2.ToString());//输出a //枚举与整型相互转换
int i = (int)h2;
hh h3 = (hh);
//也可以使用Enum中的parse方法
hh h4 = (hh)Enum.Parse(typeof(hh), "");
Console.WriteLine(h3.ToString()); //a
Console.WriteLine(h4.ToString()); //24 //枚举与字符串之间的映射,在ToString中传入D表示转换为十进制2,为G表示转换为a
string s1 = h3.ToString("D"); //
string s2 = h3.ToString("G"); //a,ToString默认是以G这种形式转换的。
hh h5 = (hh)Enum.Parse(typeof(hh), "a", false);//h5为a
//在IConvertible中还有许多To类型方法 //arrayValue指的是hh[8],arrayName指的是string[8],它们都按照整型值从小到大排列
Array arrayValue = Enum.GetValues(typeof(hh));
Array arrayName = Enum.GetNames(typeof(hh));
Console.ReadLine();
}
class Program
{
static void Main(string[] args)
{
//输出Apple,Peach
Fruit f = Fruit.Apple | Fruit.Peach;
Console.WriteLine(f.ToString());
//判断枚举组合是否包含想要的枚举值的2种方式
if (f.ToString().Contains("Apple"))
Console.WriteLine("ok");
if((Fruit.Apple&f)!=)
Console.WriteLine("ok"); //判断枚举中是否有这个成员比如葡萄Grape,可以使用IsDefined,下面的语句将返回false
bool isExist = Enum.IsDefined(typeof(Fruit), "Grape");
//可是对于组合枚举,将无法进行判断,下面两条语句将返回false、true,如果没有Mango将是false
bool isExist2 = Enum.IsDefined(typeof(Fruit), "Apple,Peach");
bool isExist3 = Enum.IsDefined(typeof(Fruit), 0x07);
//而且组合时和的组成可以有多种组成,比如这里的0x07也有可能是0x01与0x06的组合,
//因此对于判断枚举中是否可以存在这个组合枚举无法轻松判断。
Console.ReadLine();
}
}
//Flags特性是专门为枚举而提供的特性,如果没有Flags特性结果将会是7,而不是Apple,Peach
[Flags]
public enum Fruit
{
Watermelon = 0x01,
Pear = 0x02,
Apple = 0x03,
Peach = 0x04,
Mango=0x07
}
声明:本文原创发表于博客园,作者为方小白,如有错误欢迎指出 。本文未经作者许可不许转载,否则视为侵权。
C#基础之枚举的更多相关文章
- 【转】Java基础笔记 – 枚举类型的使用介绍和静态导入--不错
原文网址:http://www.itzhai.com/java-based-notes-introduction-and-use-of-an-enumeration-type-static-impor ...
- 黑马程序员:Java基础总结----枚举
黑马程序员:Java基础总结 枚举 ASP.Net+Android+IO开发 . .Net培训 .期待与您交流! 枚举 为什么要有枚举 问题:要定义星期几或性别的变量,该怎么定义?假设用1-7分别 ...
- Java基础之枚举类型Enum的使用
Java基础之枚举类型Enum的使用 定义 public enum AccruedCleanEnum { SPREAD("1","发票"),OTHER(&quo ...
- Java基础之枚举
Java基础之枚举 作为1.5才增加的特性,枚举的使用并不是很多. 枚举其实就是一个比较特殊的类,就如同注解其实也是个特殊的接口一样(注解反编译之后没有了@符号).枚举使用enum关键字声明,通过反编 ...
- java基础(十一) 枚举类型
枚举类型Enum的简介 1.什么是枚举类型 枚举类型: 就是由一组具有名的值的有限集合组成新的类型.(即新的类). 好像还是不懂,别急,咱们先来看一下 为什么要引入枚举类型 在没有引入枚举类型前,当我 ...
- C#基础_枚举
一.在学习枚举之前,首先来听听枚举的优点. 1.枚举能够使代码更加清晰,它允许使用描述性的名称表示整数值. 2.枚举使代码更易于维护,有助于确保给变量指定合法的.期望的值. 3.枚举使代码更易输入. ...
- c#编程基础之枚举
枚举的意义就在于限制变量取值范围. 当可以确定的几种取值时才可以用. 如果输入一个字符串需要进行判断是否是我们需要的字符串时,则一般需要这样写: using System; using System. ...
- Java 基础之-枚举
目录(?)[-] 用法一常量 用法二switch 用法三向枚举中添加新方法 用法四覆盖枚举的方法 用法五实现接口 用法六使用接口组织枚举 用法七关于枚举集合的使用 DK1.5引入了新的类型-- ...
- 【Java基础】枚举和注解
在Java1.5版本中,引入了两个类型:枚举类型enum type和注解类型annotation type. Num1:用enum代替int常量 枚举类型enum type是指由一组固定的常量组成合法 ...
随机推荐
- LightSpeed的批量更新和批量删除
1.Update对于批量操作 无论是Update还是Remove 都是使用LightSpeed的Query对象来完成. 注:Student是要进行Update的表(实体),StuName是表Stud ...
- APP原型设计工具,哪家强?转自知乎
著作权归作者所有. 商业转载请联系作者获得授权,非商业转载请注明出处. 作者:李志超 链接:http://www.zhihu.com/question/20403141/answer/25329730 ...
- JVM 垃圾回收算法
在说垃圾回收算法之前,先谈谈JVM怎样确定哪些对象是“垃圾”. 1.引用计数器算法: 引用计数器算法是给每个对象设置一个计数器,当有地方引用这个对象的时候,计数器+1,当引用失效的时候,计数器-1,当 ...
- 合工大OJ 1337 一加二减三
Description 题目描述:给一个串,形如一+二-三,求值 Input 第一行为一个正整数T,表示数据的组数,接下来有T行每行都是一个形如一+二-三的串,一,二,三均为正整数 Output 对于 ...
- [转载]ExtJs4 笔记(10) Ext.tab.Panel 选项卡
作者:李盼(Lipan)出处:[Lipan] (http://www.cnblogs.com/lipan/)版权声明:本文的版权归作者与博客园共有.转载时须注明本文的详细链接,否则作者将保留追究其法律 ...
- c#中结构体(struct)和类(class)的区别
一.类与结构的示例比较: 结构示例: public struct Person { string Name; int height; int weight public bool overWeight ...
- nginx反向代理+缓存开启+url重写+负载均衡(带健康探测)的部署记录
在日常运维工作中,运维人员会时常使用到nginx的反向代理,负载均衡以及缓存等功能来优化web服务性能. 废话不多说,下面对测试环境下的nginx反向代理+缓存开启+url重写+负载均衡(带健康探测) ...
- PYTHON FABRIC实现远程操作和部署
转载至:http://wklken.me/posts/2013/03/25/python-tool-fabric.html fabric title是开发,但是同时要干开发测试还有运维的活 (o(╯□ ...
- android获取当前行所属类和所属方法名
第一种方法: String Method = Thread.currentThread().getStackTrace()[2].getMethodName(); 第二种方法: priva ...
- Android Studio Jar、so、library项目依赖
Eclipse跟AS的不同 从Eclipse到AS不要带着在Eclipse中的主观色彩去在AS中使用,从项目的构成到构建是不同的,下面列举在Eclipse和AS中的一些概念的区别: WorkSpace ...