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(0)

    .field public static literal valuetype Microsoft.hh b = int32(1)

    .field public static literal valuetype Microsoft.hh c = int32(2)

    .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=2,
        b=5,
        c,
        d,
        e=1,
        f,
        g,
        h
    }

.class public auto ansi sealed hh
    extends [mscorlib]System.Enum
{
    .field public static literal valuetype Microsoft.hh a = int32(2)

    .field public static literal valuetype Microsoft.hh b = int32(5)

    .field public static literal valuetype Microsoft.hh c = int32(6)

    .field public static literal valuetype Microsoft.hh d = int32(7)

    .field public static literal valuetype Microsoft.hh e = int32(1)

    .field public static literal valuetype Microsoft.hh f = int32(2)

    .field public static literal valuetype Microsoft.hh g = int32(3)

    .field public static literal valuetype Microsoft.hh h = int32(4)

    .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)2;
            //也可以使用Enum中的parse方法
            hh h4 = (hh)Enum.Parse(typeof(hh), "24");
            Console.WriteLine(h3.ToString());   //a
            Console.WriteLine(h4.ToString());   //24

            //枚举与字符串之间的映射,在ToString中传入D表示转换为十进制2,为G表示转换为a
            string s1 = h3.ToString("D");  //2
            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)!=0)
                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
    }

声明:本文原创发表于博客园,作者为方小白 。本文未经作者同意不许转载,否则视为侵权。

时间: 2024-08-04 14:24:33

C#基础之枚举的相关文章

c#编程基础之枚举

枚举的意义就在于限制变量取值范围. 当可以确定的几种取值时才可以用. 如果输入一个字符串需要进行判断是否是我们需要的字符串时,则一般需要这样写: using System; using System.Collections.Generic; using System.Text; namespace 枚举学习 { class Program { static void Main(string[] args) { string s = "Male"; if (s == "Male

【转】Java基础笔记 – 枚举类型的使用介绍和静态导入--不错

原文网址:http://www.itzhai.com/java-based-notes-introduction-and-use-of-an-enumeration-type-static-import.html#1.2.values方法的使用: Java基础笔记 – 枚举类型的使用介绍和静态导入 本文由arthinking发表于4年前 | Java基础 | 暂无评论 |  被围观 8,332 views+ 1.枚举(Enum):1.1.枚举类型中的两个静态方法:1.2.values方法的使用:

C#语言基础-3 枚举和名称空间

1.枚举  枚举是用户定义的整数类型,在声明时指定该枚举类型的实例可以包含的一组可接受的值.  代码编译好之后,枚举就成为基本类型.  枚举继承于基类Sysytem.Enum的结构,表示可以对枚举调用方法.把枚举当做结构不会造成性能损失.  语法定义  enum 枚举名  {      枚举值   }  每个枚举值对应一个数字值,默认从0开始依次加1,如果其中某一个赋值后,后面的值在赋值的基础上依次加1.允许赋值为Int类型的数值,包含负整数.值允许重复  例如:需要使用周一到周日时,可以声明个

【JAVA基础】枚举

枚举 将一组有限集合创建为一种新的类型,集合里面的值可以作为程序组件使用: 枚举基本特性 以下代码是枚举的简单使用: 使用values方法返回enum实例的数组 使用ordinal方法返回每个enum实例的次序,从0开始 使用compareTo方法比较两个enum实例 使用==来比较enum实例 使用getDeclaringClass方法返回enum实例所属的enum类 使用valueOf方法根据指定的名字返回enum实例 package enumerated; enum Shrubbery {

java基础篇---枚举详解

在JDK1.5之前,JAVA可以有两种方式定义新类型:类和接口,对于大部分面向对象编程,有这两种似乎就足够了,但是在一些特殊情况就不合适.例如:想要定义一个Color类,它只能有Red,Green,Blue三种,其他值则是错误,在JDK1.5之后便引入枚举类型. 枚举其实就是一种类型,跟int, char 这种差不多,就是定义变量时限制输入的,你只能够赋enum里面规定的值. public enum Color{ RED,GREEN,BLUE ; // 定义三个枚举的类型 }; 枚举中有三个取值

java 基础之枚举

问题:对象的某个属性的值不能是任意的,必须为固定的一组取值其中的某一个 解决办法: 1)  在setGrade方法中做判断,不符合格式要求就抛出异常 2)  直接限定用户的选择,通过自定义类模拟枚举的方式来限定用户的输入 写一个Grade类,私有构造函数,对外提供5个静态的常量表示类的实例 3)  jdk5中新定义了枚举类型,专门用于解决此类问题 4)  枚举就是一个特殊的java类,可以定义属性.方法.构造函数.实现接口.继承类 packagecn.itcast.enumeration; im

java 基础(枚举类)

1.枚举类也是一个类,也可以有属性,等等: public enum SeasonEnum{ SPRING, SUMMER, FALL, WINTER; } 这个是没有其他属性的. package com.creditharmony.adapter.testCase.test; public class EnumTest { public void judge(SeasonEnum s) { switch (s) { case SPRING: System.out.println("春天"

Java基础之枚举妙用

对于枚举,初学Java的时候可能我们就已经接触过了,但是在毕业前,其实一直都不知道真正工作里面枚举是怎么用的,枚举有什么用?接下来,博主就介绍枚举在实际工作中的一种使用场景,本文只适合初级的小菜鸟看哈,大神看了可别嘲笑这是水文哦,哈哈! 一.使用场景 在实际的工作中,比如我有一个选择家庭关系的下拉选择框,大家都是会是value和desc的组合,一般我们保存到数据库的都是value(英文),然后用户看到的是desc(中文).但是我很多个页面会用到这样的下拉框,甚至可能很多个系统会用到,需要保持数据

Java 基础之-枚举

目录(?)[-] 用法一常量 用法二switch 用法三向枚举中添加新方法 用法四覆盖枚举的方法 用法五实现接口 用法六使用接口组织枚举 用法七关于枚举集合的使用 DK1.5引入了新的类型--枚举.在 Java 中它虽然算个"小"功能,却给我的开发带来了"大"方便. 在JDK1.5 之前,我们定义常量都是: public static fianl.... .现在好了,有了枚举,可以把相关的常量分组到一个枚举类型里,而且枚举提供了比常量更多的方法. Java代码 pu

黑马程序员-基础加强-枚举

枚举: Java 中的枚举类型采用关键字enum 来定义,从jdk1.5才有的新类型,所有的枚举类型都是继承自Enum 类型.枚举类型的定义也非常的简单,用 enum 关键字加上名称和大括号包含起来的枚举值体即可,例如上面提到的彩虹颜色就可以用新的 enum 方式来重新定义: enum RainbowColor { RED, ORANGE, YELLOW, GREEN, CYAN, BLUE, PURPLE } 从上面的定义形式来看,似乎 Java 中的枚举类型很简单,但实际上 Java 语言规