• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
朽木不可雕也!黄金不能吃亦
博客园    首页    新随笔    联系   管理    订阅  订阅
第五章 枚举、结构体和数组

1.类型

值类型

变量直接包含它们自己的数据

局部变量总是放在栈(stack)中

引用类型

变量间接指向它们的数据

局部变量指向堆(heap)中的对象

枚举(enum)    值类型

结构(struct)    值类型

类(class)       引用类型

接口(interface) 引用类型

数组([ ]array )   引用类型

委托(delegate)   引用类型

你可能对上面的例子感到奇怪,c#中的内在类如int,double怎么没有。C#规定这些内在类属于结构,C#称之为简单类型。简单类型和用户自定义类型之间的最大区别是前者有字面表达式,而后者没有。

当然,还有第三种类型:指针。但指针只用在由unsafe关键字标识的非安全的代码中。

2.枚举类型

·       它是一个用户声明的值类型

enum Suit
{
    Clubs, Diamonds, Hearts, Spades
}
//Suit表示一副牌,它有4个花色:梅花(Clubs),方块(Diamonds),红心(Hearts),//黑桃(Spades)
sealed class Example
{
    static void Main()
    {
        ...
        Suit lead = Spades; //错误
        ...
        Suit trumps = Suit.Clubs; //正确
        ...
    }
}

枚举的声明可以出现在类声明的相同地方。

枚举的声明包括名字、访问权限、内在的类型和枚举的成员。

枚举中声明的常量的范围是定义它们的枚举,换言之,下面的例子是错误的:

              Suit trumps = Clubs;

Clubs必须被限制为Suit的一个成员,就如下面:

       Suit trumps = Suit.Clubs;

3.枚举的注意点

 枚举值缺省为int

你可以选择任一内在的整数类型

但不能是字符型

enum Suit : int //内在类型是int,可以省略
    {
        Clubs,
        Diamonds,
        Hearts = 42, //成员的取值缺省为前一个成员取值+1,但可以自己赋初值
        Spades, //最后一个分号是可选的
    };//可以有结尾分号

枚举类可以显式的声明它的内在类型是sbyte, byte, short, ushort, int, uint, long, ulong。如果一个枚举类没有显式声明它的内在类型,则缺省为int。

成员的取值必须和枚举声明的内在类型相同,并且必须在内在类型的范围之内(例如,你不能让成员的取值为负数,而枚举的内在类型是uint)。

如果成员没有被赋值, 那么它的取值是前一个成员取值+1,第一个成员的缺省值是1。枚举的成员的取值可以有相同的取值。

最后一个枚举成员可以使用一个结尾分号,这使得你将来可以很方便地加入更多的成员。

枚举成员的访问权限隐含为public

以下是使用枚举时几条好的建议。

优先考虑使用枚举,而不是类的静态常量

比如:

public static class Day
{
    public static int Sun = 1;
    public static int Mon = 2;
    public static int Tue = 3;
    //...
}

应该使用如下的枚举:

enum Day { Sun = 1, Mon, Tue, Wed, Thu, Fri, Sat };

如果参数、返回值、变量等类型可以是枚举,则不要使用其它基础类型

比如:

Range r = Range.MAX; //好
int r = (int)Range.MAX; //不好

枚举命名

枚举一般使用名词或名词组合,简单枚举使用单数,标志枚举使用复数。

大多数情况下不需要更改枚举的默认类型

也就是说大多数情况下,使用 int(System.Int32)作为枚举类型。除非:

  • 枚举是标志枚举,且标志多于 32 个(此时 int 类型装不下)。
  • 枚举被非常大量且频繁地使用,为了节约空间使用小于 int 的类型。
  • 不得不使用其它类型的情况。

不要在枚举中设置哨兵

我们可能觉得在枚举的两端加上哨兵,这样在判断一个数是否在枚举中时,只需要判断是否在哨兵之中。非常不幸,我们不应该这么做,这破坏了枚举的意义。

enum Day {FirstValue, Sun, Mon, Tue, Wed, Thu, Fri, Sat, LastValue }; //FirstValue、LastValue 应该去掉

  • 简单枚举包含的值不用于组合,也不用于按位比较。
  • 标志枚举应使用按位 OR 操作进行组合。

简单枚举

前面提到的 Day、Range 都可以称之为简单枚举,因为不能将他们各自的值组合起来。

标志枚举

标志枚举的设计有两点要注意。

  • 指明 FlagsAttribute,以指示可以将枚举作为位域(即一组标志)处理。
  • 枚举中各标志的值应该是以 2 的幂来赋值,即:1、2、4、8、16、32……

举个例子,假如我们在设计 Windows 窗口程序,窗口有最小化、最大化、关闭按钮,我们想任意组合显示,也就是说我们可以显示其中的任意 0 个或一个或多个按钮。

如果使用简单枚举,按照排列组合,我们要使用 1 + 3 + 3 + 1 = 8 个枚举数,如果这里不是三个按钮,而是四个按钮,枚举数就更多了。所以这样不现实。

为什么这里使用简单枚举不现实呢?因为简单枚举不能组合,采用标志枚举就可以轻松解决了。

[Flags]
public enum WindowStyle
{
    MINIMUM_BUTTON = 1, //十六进制表示为 0x0001
    MAXIMUM_BUTTON = 2,
    CLOSE_BUTTON = 4
}

我们在设置窗口样式时,利用 OR 自由组合:

WindowStyle ws = WindowStyle.MINIMUM_BUTTON | WindowStyle.CLOSE_BUTTON; //表示既有 MINIMUM_BUTTON 也有 CLOSE_BUTTON

这就是为什么标志的值要按 2 的幂排列的原因了,也是为什么标志多于 32 个时不能使用 int 类型的原因了。

通常我们为常用的标志组合提供特殊的枚举值

仍然以上述窗口为例,可知大多数情况下,我们均要显示这三个按钮,所以每次使用时都要用:

WindowStyle ws = WindowStyle.MINIMUM_BUTTON | MAXIMUM_BUTTON | WindowStyle.CLOSE_BUTTON;

实在有些繁琐,我们可以修改枚举为如下:

[Flags]
public enum WindowStyle
{
    MINIMUM_BUTTON = 1,
    MAXIMUM_BUTTON = 2,
    CLOSE_BUTTON = 4,
    ALL_BUTTON = 7
}

增加一个 ALL_BUTTON 为前三个标志的值。使用时直接用 ALL_BUTTON 就可以了。

 

 

C#结构体和类的区别问题

在C#编程语言中,类属于引用类型的数据类型,结构体属于值类型的数据类型,这两种数据类型的本质区别主要是各自指向的内存位置不同。传递类的时候,主要表现为是否同时改变了源对象。

C#结构体和类的区别技术要点:

◆类在传递的时候,传递的内容是位于托管内存中的位置,结构体在传递的时候,传递的内容是位于程序堆栈区的内容。当类的传递对象修改时,将同时修改源对象,而结构体的传递对象修改时,不会对源对象产生影响。

◆在一个类中,可以定义默认的、不带参数的构造函数,而在结构体中不能定义默认的、不带参数的构造函数。两者都可以定义带有参数的构造函数,通过这些参数给各自的字段赋值或初始化。

C#结构体和类的区别之实现步骤

(1)创建控制台应用程序项目,命名为“ClassAndStruct”。

(2)打开并编辑Program.cs文件,代码如下所示。

using System;
using System.Collections.Generic;
using System.Text;

namespace ClassAndStruct
{
    class program
    {
        static void Main(string[] args)
        {   
            //使用带参数的构造函数创建员工类的实例   
            classEmployee clsEmpA = new classEmployee("Pony","Smith",43);   
            classEmployee clsEmpB = clsEmpA;   
            //修改引用数据   
            clsEmpB.Age = 33;   
            //使用带参数的构造函数创建员工结构体的实例   
            structEmployee strctEmpA = new structEmployee("Pony", "Smith", 43);   
            structEmployee strctEmpB = strctEmpA;   
            //修改   
            strctEmpB.Age = 33;   
            Console.WriteLine("类的数据:姓名-{0} {1} 年龄-{2}",    clsEmpA.FirstName,clsEmpA.LastName,clsEmpA.Age);   
            Console.WriteLine("结构体的数据:姓名-{0} {1} 年龄-{2}",    strctEmpA.FirstName, strctEmpA.LastName, strctEmpA.Age);   
            Console.ReadLine();    }    }    class classEmployee
            //表示员工的类   
            {   
                private string firstname;   
                public string FirstName   
                {   
                    get
                    {
                        return firstname;
                    }   
                    set
                    {
                        firstname = value;
                    }   
                }   
                private string lastname;   
                public string LastName   
                {   
                    get
                    {
                        return lastname;
                    }   
                    set
                    {
                        lastname = value;
                    }   
                }   
                private int age;   
                public int Age   
                {   
                    get
                    {
                        return age;
                    }   
                    set
                    {
                        age = value;
                    }   
                }  
                //类的默认构造函数,可以在类中重新定义   
                public classEmployee()   
                {   
                    firstname = "";    lastname = "";    age = 0;  
                }   
                //C#结构体和类的区别
                //类的带参数的构造函数,在构造类实例的同时给字段赋值
                public classEmployee(string strFirstNamem, string strLastName, int iAge)  
                {   
                    firstname = strFirstNamem;
                    lastname = strLastName; 
                    age = iAge;  
                } 
            }   
    struct structEmployee
        //表示员工的结构体  
    {  
        private string firstname;  
        public string FirstName 
        {  
            get
            {
                return firstname;
            }   
            set
            {
                firstname = value;
            }  
        }  
        private string lastname; 
        public string LastName
        { 
            get
            {
                return lastname;
            }  
            set
            {
                lastname = value;
            }  
        }
        //C#结构体和类的区别
        private int age;
        public int Age 
        {   
            get
            {
                return age;
            }   
            set
            {
                age = value;
            } 
        }   
        //在结构体中不能定义默认的不带参数的构造函数,只能定义结构体的带参数的构造函数
        public structEmployee(string strFirstNamem, string strLastName, int iAge)  
        {   
            firstname = strFirstNamem; 
            lastname = strLastName; 
            age = iAge; 
        }
        }
    }
}

    (3)按F5键运行程序,运行结果如下所示。

    类的数据:姓名-Pony Smith 年龄-33

    结构体的数据:姓名-Pony Smith 年龄-43

    C#结构体和类的区别之源程序解读

    (1)本示例为了说明在传递时C#结构体和类的区别,在程序中分别定义了表示员工的类classEmployee类和表示员工的结构体structEmployee,并定义了各自的字段和构造函数。在主程序入口Main方法中,声明类的实例clsEmpA和clsEmpB,并使用构造函数创建clsEmpA类实例,然后将clsEmpA类实例传递给clsEmpB类实例,修改clsEmpB类实例的字段值,最后打印clsEmpA类实例中的字段,查看字段的值是否随clsEmpB类实例字段的修改而变化。同时,声明结构体的实例strctEmpA和strctEmpB,并使用构造函数创建strctEmpA结构体实例,然后将strctEmpA结构体实例传递给strctEmpB结构体实例,修改strctEmpB结构体实例的字段值,最后打印strctEmpA结构体实例中的字段,查看字段的值是否随strctEmpB结构体实例字段的修改而变化。程序的流程图如图8.1所示。 

    (2)C#结构体和类的区别还有一个比较明显的区别,就是类能够定义默认的、不带参数的构造函数,并能在该构造函数中初始化字段。而结构体不允许定义默认的、不带参数的构造函数。

     

    数组是具有相同数据类型的项的有序集合。要访问数组中的某个项,需要同时使用数组名称及该项与数组起点之间的偏移量。在 C# 中,声明和使用数组的方法与 Java 有一些重要区别。

    一维数组 
    一维数组以线性方式存储固定数目的项,只需一个索引值即可标识任意一个项。在 C# 中,数组声明中的方括号必须跟在数据类型后面,且不能放在变量名称之后,而这在 Java 中是允许的。因此,类型为 integers 的数组应使用以下语法声明: 
    
    int[] arr1; 
    下面的声明在 C# 中无效: 
    //int arr2[]; //compile error 
    声明数组后,可以使用 new 关键字设置其大小,这一点与 Java 相同。下面的代码声明数组引用: 
    int[] arr; 
    arr = new int[5]; // create a 5 element integer array 
    然后,可以使用与 Java 相同的语法访问一维数组中的元素。C# 数组索引也是从零开始的。下面的代码访问上面数组中的最后一个元素:
    Console.WriteLine(arr[4]); // access the 5th element 
    初始化 
    int[] arr2Lines; 
    arr2Lines = new int[5] {1, 2, 3, 4, 5}; 
     C# 初始值设定项的数目必须与数组大小完全匹配,这与 Java 不同。可以使用此功能在同一行中声明并初始化 C# 数组: 
    int[] arr1Line = {1, 2, 3, 4, 5}; 
    此语法创建一个数组,其大小等于初始值设定项的数目。 
    在程序循环中初始化 
    在 C# 中初始化数组的另一个方法是使用 for 循环。下面的循环将数组的每个元素都设置为零:
    int[] TaxRates = new int[5]; 
    for (int i=0; i<TaxRates.Length; i++) 
    { 
    TaxRates[i] = 0; 
    } 
    交错数组 
    C# 支持创建交错(非矩形)数组,即每一行包含的列数不同的数组。例如,在下面的交错数组中,第一行有四项,而第二行有三项: 
    
    int[][] jaggedArray = new int[2][]; 
    jaggedArray[0] = new int[4]; 
    jaggedArray[1] = new int[3]; 
    多维数组 
    可以使用 C# 创建规则的多维数组,多维数组类似于同类型值的矩阵。 C# 还支持多维数组(数组的数组)。 
    
    使用以下语法声明多维矩形数组: 
    
    int[,] arr2D; // declare the array reference 
    float[,,,] arr4D; // declare the array reference 
    声明之后,可以按如下方式为数组分配内存: 
    
    arr2D = new int[5,4]; // allocate space for 5 x 4 integers 
    然后,可以使用以下语法访问数组的元素: 
    
    arr2D[4,3] = 906; 
    由于数组是从零开始的,因此此行将第四行第五列中的元素设置为 906。 
    
    初始化 
    可以使用以下一种方法,在同一个语句中创建、设置并初始化多维数组: 
    
    int[,] arr4 = new int [2,3] { {1,2,3}, {4,5,6} }; 
    int[,] arr5 = new int [,] { {1,2,3}, {4,5,6} }; 
    int[,] arr6 = { {1,2,3}, {4,5,6} }; 
    posted on 2009-11-08 23:06  大牙  阅读(958)  评论(0)    收藏  举报
    刷新页面返回顶部
    博客园  ©  2004-2025
    浙公网安备 33010602011771号 浙ICP备2021040463号-3