结构
值类型中除了枚举类型都是结构类型的派生类型
由于结构是值类型,并且直接存储数据,因此,在一个对象的主要成员为数据且数据量不大的情况下,使用结构会带来更好的性能。
public struct Address
{
public string Name;
public string Phone;
}
Address address;
address.Name = “中国”;
address.Phone = “12345678”;
结构是值类型,直接包含它自己的数据,每个结构都保存自己的一份数据,修改每一个结构的数据都不会对其他结构的数据造成影响,结构不是引用类型,因此结构类型的变量不能被赋予null值。
把一个结构赋值给另一个结构时,会把数据从一个结构复制到另一个结构,因此,当结构比较大的时候,这种数据复制机制会带来较大的性能开销。
无论结构使用预定义的、无参数的构造函数,还是使用用户定义的、有参数的构造函数进行初始化,都会初始化结构的数据成员。其中前者将数值型初始化为默认值,引用类型初始化为null,后者将使用用户自定义的初始化策略对种成员进行初始化。因此,结构类型不允许在声明时显式初始化数据成员。
构造函数
结构类型可以有实例构造函数和静态构造函数,但不能有析构函数(析构函数主要针对非托管代码中引用的释放)。
实例构造函数
结构类型都有一个预定义的、没有参数的构造函数,此构造函数不允许删除和重定义,并且这个无参数的构造函数将会一直存在,并不会因为定义了其他带参数的构造函数就消失,与类不同。
要调用结构实例构造函数,必须使用new运算符。如果不使用new运算符,那么结构的数据成员就是未分配状态,在分配之前是不能访问的。
带参数构造函数必须初始化结构的所有数据成员,并保证数据成员确实被初始化(做参数检测,确保成员被初始化,否则,未正确初始化将会导致编译错误)
静态构造函数
结构的静态构造函数具有如下特点:
- 不能有访问修饰符和参数
- 不能访问实例成员
- 无法直接进行调用
结构和类的静态构造函数的触发规则不同,类的静态构造函数是在创建第一个实例或引用任何静态成员之前自动调用的,而结构的静态构造函数在下述任意一种情况之前才会被自动调用:
- 使用显式声明的构造函数进行初始化
- 调用结构的方法或访问结构的静态数据成员(无论读取还是赋值,访问实例数据成员不会触发CLR自动调用静态构造函数)
多态和可继承性
结构直接派生自System.ValueType,间接派生自System.Object,但结构是隐式密封的,不能作为基类再派生出其他的结构,也不能从类派生,但可以从接口派生。
关于结构的继承性,有如下特点:
- 结构类型总是隐式密封,因此在定义结构时不能使用sealed和abstract关键字
- 因为结构不能作为基类,结构的成员不能使用如下访问修饰符:protected和protected internal
- 结构的函数成员不能声明为abstract和virtual,但是可以使用override关键字,用以覆写它的基类System.ValueType中的方法
装箱与拆箱
和类的类型转换最大的不同是,装箱和拆箱操作执行的是将结构复制到或复制出装箱的实例对象。即装箱前的数据和拆箱后的数据是两份不同的数据,没有直接关联,修改其中一个不会影响另一个。
结构和类的适用场合
- 当堆栈的空间很有限,且有大量的逻辑对象时,创建类要比创建结构好一些
- 对于点、矩形和颜色这样的轻量对象,使用结构的成本较低
- 在表现抽象和多级别的对象层次时,类是最好的选择,因为结构不支持继承
- 大多数情况下,目标类型只是含有一些数据,或者以数据为主,例如字典中的“key-value”数据对,这时使用结构是最佳选择
引用
[1]: C# 权威指南