结构
结构属于值类型,也就是存储在堆栈中(详细讲解请看2.3.3值类型和引用类型),通过这种方式可以在数据的生存期上获得很大的灵活性,但性能会有一定的损失。因为托管堆的优化,这种性能损失比较小。
结构和类一样可以拥有成员,包括:构造函数、方法、属性、字段、常量、枚举类型、事件、以及事件处理函数等。
有时我们需要一个小的数据结构,此时,类提供的功能多于我们需要的功能,由于性能的原因,我们最好使用结构。
例如,我们需要一个结构来记录点的坐标,我们可以定义下面的结构,而不需要定义一个类:
1 struct Point 2 { 3 // 点的横坐标 4 public double X { get; set; } 5 // 点的纵坐标 6 public double Y { get; set; } 7 // 点的坐标信息 8 public string PointInfo 9 { 10 get 11 { 12 return "(" + this.X + ":" + this.Y + ")"; 13 } 14 } 15 }
结构使用关键字struct进行定义,结构具有以下特点:
- 属于值类型,派生自System.ValueType
- 结构不支持继承(所以结构中成员的访问修饰符不能是protected)
- 结构可以实现接口
- 对于结构,构造函数的工作方式与引用类型的构造函数有一些区别,结构提供了一个没有参数的默认构造函数,这个默认的构造函数不允许替换
- 在结构中定义字段时,不可以对其进行初始化,必须通过构造函数对字段进行初始化
1.结构不支持继承
结构不是为继承设计的。这以为着:它不能从一个结构中继承。但是,结构最终派生于System.Object类。结构是一个值类型,所有值类型都派生自System.ValueType类,而System.ValueType类又派生自System.Object类。所以我们可以调用甚至重写System.Object中的一些方法。
2.结构默认的无参构造函数不允许替换
也就是不允许用户在结构内部显式定义无参的构造函数。如果我们结构定义了无参的构造函数就会报错:
1 struct Point 2 { 3 //为结构显示定义一个无参的构造函数----这是错误的做法 4 public Point() 5 { 6 7 } 8 // 点的横坐标 9 public double X { get; set; } 10 // 点的纵坐标 11 public double Y { get; set; } 12 // 点的坐标信息 13 public string PointInfo 14 { 15 get 16 { 17 return "(" + this.X + ":" + this.Y + ")"; 18 } 19 } 20 }
错误:
3.在结构中定义字段时,不可以对其进行初始化,必须通过构造函数对字段进行初始化
在2.1 变量的声明及初始化中讲到,默认构造函数会将值类型的字段初始化为对应类型的默认值,把引用类型的字段初始化为Null,即使提供了其他带参数的构造函数,也是如此。为字段提供初始值也会被构造函数改变,所以如果为结构中的字段设置初始值就会报错:
1 struct Point 2 { 3 // 点的横坐标 4 public double X = 200; 5 // 点的纵坐标 6 public double Y = 300; 7 // 点的坐标信息 8 public string PointInfo 9 { 10 get 11 { 12 return "(" + this.X + ":" + this.Y + ")"; 13 } 14 } 15 }
错误:
虽然结构是值类型,但是在语法上有与引用类型非常相似,例如在定义时:
1 Point point = new Point(); 2 point.X = 200; 3 point.Y = 800;
但是我们应该注意的是,结构是一个值类型,所以关键字new和其他应用类型的工作方式不同;结构中的关键字new不在托管堆中分配内存空间,而是直接调用相应的构造函数,根据传递的参数对字段进行初始化,然后将其压入堆栈中。
结构和类的区别:
- 结构是值类型,类是引用类型;这就导致结构存储在堆栈中,而类存储在托管堆中;
- 结构不能继承或被继承,类可以继承或被继承;这就导致结构中的成员不能使用proteccted修饰;
- 结构中不能在定义字段是为其赋初始值,类可以
- 结构不可以显示定义无参的默认构造函数,类可以