一、结构(Struct)是CTS中五种基本类型之一,是一种值类型,同样封装了同属一个逻辑单元的数据和行为,这些数据和行为通过结构中的成员表示;结构与类共享大多数相同的语法,但结构比类受到的限制更多,结构适用于表示轻量级类型;使用struct关键字定义结构:
//定义一个公共结构MyStruct public class MyStruct { public int MyField; //声明一个int类型的公共实例字段 public void MyFunc() //声明一个公共实例方法 { //do… } }
1.所有结构都直接隐式继承自System.ValueType,不能再指定继承自其它任何结构或类,即结构不支持继承,但可以实现一个或多个接口,同时结构也是隐式密封的,不能被继承;
※将结构类型的对象强制转换为其所实现的任何接口类型或object类型时会导致装箱操作,此时会将结构类型的对象包装到托管堆内存上的引用类型对象内;
2.由于结构不支持继承,因此结构不能被定义为abstract或sealed;
3.结构不能被定义为静态的,但可以声明静态成员;
4.由于结构不支持继承,因此结构成员只能声明为public、internal或private的,不能声明为abstract、virtual和sealed;
※对于实例成员,不能在结构声明中直接对其进行初始化;
※对于静态成员,可以在声明时进行初始化,也可以在静态构造函数中对其初始化;
※结构中不能声明默认构造函数(无参数的构造函数),结构的默认构造函数由编译器保留,并一直处于可用状态,其作用是申请指定大小的内存空间,并将所有字节初始化为0(即default(T));
※结构中可以声明带参数的自定义构造函数,自定义构造函数的方法体中必须对所有的实例成员进行初始化,否则编译器会报错,私有实例成员只能在构造函数中进行初始化;
※结构不存在析构阶段,不能声明析构函数;
public struct MyStruct { public int MyNum; public string MyStr; public static int MyStaticNum = 1; public MyStruct(int myNum,string myStr) { MyNum = myNum; MyStr = myStr; } static MyStruct() { //对于静态成员,可以直接在声明时初始化,也可以在静态构造函数中初始化 //MyStaticNum = 1; } }
二、可以通过调用默认构造函数、自定义构造函数、使用对象初始化器或在声明结构之后单独初始化成员的方式构建结构实例;
1.使用运算符new或运算符default构建结构实例,并调用对应的构造函数:
MyStruct myStruct = new MyStruct(); //调用结构的默认构造函数,此时MyNum为0,MyStr为null myStruct = default(MyStruct); //与使用new MyStruct()完全等效 myStruct = new MyStruct(1, "1"); //调用结构的自定义构造函数
2.与类不同,结构的实例化可以不使用new运算符,此时不会调用任何构造函数,也不会初始化任何实例成员,内存分配效率提高,在访问某个实例字段之前对该字段初始化即可:
MyStruct myStruct; //构建结构实例,但不调用构造函数 myStruct.MyNum = 1; int myNum = myStruct.MyNum; //访问某个实例成员之前需要对其初始化
※通常适用于只使用结构中部分实例字段进行存储和操作的情况;
※只有当所有的实例字段都初始化完成后,才能调用其实例方法或将其用作参数、返回值;
※结构中存在私有实例字段时也可以使用此种方式构建结构实例,但也意味着不能初始化完成其所有实例字段;
三、结构是值类型,变量和数据放在一起,对结构类型的变量进行赋值、传递参数、方法返回等操作时都会产生新的变量,并会复制(即浅拷贝)原变量中的所有数据到新变量中,对新变量所做的任何修改都不会改变原变量的数据,只能将新变量重新赋值给原变量,在处理值类型的集合(如List<MyStruct>)时需要格外注意这点:
//当需要修改值类型集合中某个元素的数据时,需要先拿一个变量接收,修改完成后再赋值给集合 MyStruct myStruct = myStructList[0]; myStruct.MyNum = 20; myStructList[0] = myStruct;
1.结构类型可用作可空类型,此时依然是值类型,可空类型的变量可赋值为null;
MyStruct? myStruct = null;
四、自定义结构的最佳实践:
public struct MyStruct : IEquatable<MyStruct> //实现IEquatable<T>接口用于泛型 { public int MyNum; public override bool Equals(object obj) //会对实参进行装箱 { if (!(obj is MyStruct)) { return false; } MyStruct other = (MyStruct)obj; //拆箱 return this.Equals(other); } public override int GetHashCode() //避免使用散列集合类时装箱并提供高效实现 { return MyNum.GetHashCode(); } public override string ToString() //避免装箱 { return MyNum.ToString(); } public bool Equals(MyStruct other) //避免比较时实参装箱,避免使用泛型时装箱 { return this.MyNum == other.MyNum; } public static bool operator ==(MyStruct left, MyStruct right) //比较时通常采用==运算符 { return left.Equals(right); } public static bool operator !=(MyStruct left, MyStruct right) { return !(left == right); } }
※如果需要进行大小比较,还应该实现接口IComparable<T>并重载运算符<=和>=;
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的认可是我写作的最大动力!
作者:Minotauros
出处:https://www.cnblogs.com/minotauros/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。