
4.1 结构
结构是一种值类型,通常用来封装小型相关变量组。例如,矩形的坐标或者库存商品的特征。本节将介绍与结构有关的内容,包括结构的定义、结构的成员和如何使用等。
4.1.1 定义结构
C#中的类是引用类型,而结构是值类型。C#中定义结构需要用struct关键字。定义语法如下:
[struct-modifiers] struct identifier [struct-interfaces]{ struct-body; }
其中,struct-modifiers是一个可选参数,表示结构可用修饰符,例如public、internal和private等,默认值为public。identifier表示结构的名称。struct-interfaces是可选参数,表示结构的基接口。struct-body表示结构体,即结构的内容。
提示
一般情况下,很少使用结构,而且很多人都不建议使用结构。但是它作为.NET Framework的一部分,还是有必要了解一下的。
【范例1】
创建名称为Book的结构,该结构包含三个成员,代码如下。
public struct Book { public decimal price; public string title; public string author; }
4.1.2 结构成员
结构的成员和类很相似,可包含构造函数、常量、字段、属性、方法、事件、索引器、运算符和嵌套类型等。
(1)常量:用来表示常量的值。
(2)字段:结构中声明的变量。
(3)属性:用于访问对象或结构的特性的成员。
(4)方法:包含一系列语句的代码块,通过这些代码块能够实现预先定义的计算或操作。
(5)事件:一种使对象或结构能够提供通知的成员。
(6)索引器:又被称为含参属性,是一种含有参数的属性。提供以索引的方式访问对象。
(7)运算符:通过表达式运算符可以对该结构的实例进行运算。
(8)构造函数:包括静态构造函数和实例构造函数,静态构造函数使用static修饰;实例构造函数不必使用static修饰符。
注意
在结构中,如果同时需要上述几种成员,那么可以考虑使用类作为类型,而不再是结构。
开发者为结构显式定义无参构造函数是错误的,在结构体中初始化实例字段也是错误的。只能通过两种方式初始化结构成员:一是使用参数化构造函数;二是在声明结构后分别访问成员。对于任何私有成员或以其他方式设置为不可访问的成员,只能在构造函数中进行初始化。
如果使用new关键字创建结构对象,则会创建该结构对象,并调用适当的构造函数。与类不同,结构的实例化可以不使用new关键字,在这种情况下不存在构造函数调用,因此可以提高分配效率。但是,在初始化所有的字段之前,字段将保持未赋值状态且对象不可用。
当结构包含引用类型作为成员时,必须显式调用该成员的默认构造函数,否则该成员将保持未赋值状态且结构不可用。
【范例2】
本范例演示使用默认构造函数和参数化构造函数的struct初始化。实现步骤如下。
(1)创建名称是CoOrds的结构,在该结构中定义两个字段x和y,并在有参数的构造函数中对x和y初始化,代码如下。
public struct CoOrds { public int x, y; public CoOrds(int p1, int p2) { x = p1; y = p2; } }
(2)向Main()方法中添加代码,首先分别实例化结构的无参和有参的构造函数,然后分别输出两个字段的值,代码如下。
static void Main(string[] args) { CoOrds coords1 = new CoOrds(); CoOrds coords2 = new CoOrds(10, 10); Console.Write("CoOrds 1:"); Console.WriteLine("x = {0}, y = {1}", coords1.x, coords1.y); Console.Write("CoOrds 2:"); Console.WriteLine("x = {0}, y = {1}", coords2.x, coords2.y); Console.ReadLine(); }
(3)运行上述代码查看输出结果,内容如下。
CoOrds 1:x = 0, y = 0 CoOrds 2:x = 10, y = 10
【范例3】
本范例说明结构特有的功能,即结构在不使用new的情况下创建CoOrds对象。实现步骤如下。
(1)创建名称是CoOrds的结构,在该结构中声明两个字段x和y,在有参的构造函数中为两个字段赋值。
(2)向Main()方法中添加代码,首先声明CoOrds的结构对象,然后分别为该对象的x和y赋值,最后通过“对象名.字段”输出字段的值,代码如下。
static void Main(string[] args) { CoOrds coords1; coords1.x = 10; coords1.y = 20; Console.Write("CoOrds 1:"); Console.WriteLine("x = {0}, y = {1}", coords1.x, coords1.y); }
(3)运行上述代码查看输出结果,内容如下。
CoOrds 1:x = 10, y = 20
4.1.3 结构和类
结构与类共享大多数相同的语法,例如它们都可以实现接口、都拥有相同的成员、成员都有各自的存取范围和都可以声明和触发事件等。对于它们的不同点,表4-1分别从类型、分配空间、结构成员、构造函数、析构函数、初始化变量和基类等方面进行说明。
表4-1 结构和类的主要区别

结构和类各有各的优势,那么何时使用结构,何时使用类呢?下面根据不同的情况进行说明。
(1)堆栈的空间有限,对于大量的逻辑对象,创建类要比创建结构好一些。
(2)大多数情况下该类型只是一些数据时,结构是最佳的选择,否则使用类。
(3)在表现抽象或者多层次的数据时,类是最好的选择。
(4)如果该类型不继承自任何类型时使用结构,否则使用类。
(5)该类型的实例不会被频繁地用于集合中时使用结构,否则使用类。