基础系列(7)—— 结构
一 结构的定义
结构用 struct 关键字定义的,与类相似,都表示可以包含数据成员和函数成员的数据结构。数据成员主要包括常量、变量、构造函数、方法、属性等。同时结构的特征为: 结构是一种值类型,并且不需要堆分配。 结构的实例化可以不使用 new 运算符。可以看做是“轻量级的类”
(1)定义结构的语法:
<访问修饰符> struct 结构名 { //结构体 }
(2)什么时候使用结构
结构体适合一些小型数据结构,这些数据结构包含的数据以创建结构后不修改的数据为主例如:struct类型适于表示PointRectangle和Color等轻量对象。尽管可以将一个点表示为类,但在某些情况下,使用结构更有效。如果声明一个10000个Point对象组成的数组,为了引用每个对象,则需分配更多内存;这种情况下,使用结构可以节约资源。定义的时候不会用到面向对象的一些特性;结构体在不发生装箱拆箱的情况下性能比类类型是高很多的.
在结构声明中,除非字段被声明为 const 或 static,否则无法初始化赋值。 结构类型永远不是抽象的,并且始终是隐式密封的,因此在结构声明中不允许使用abstract和sealed修饰符。结构不能声明默认构造函数(没有参数的构造函数)或析构函数,但可以声明带参数的构造函数。 结构可以实现接口,但不能从另一个结构或类继承,而且不能作为一个类的基,所有结构都直接继承自System.ValueType,后者继承自 System.Object。 结构在赋值时进行复制。 将结构赋值给新变量时,将复制所有数据,并且对新副本所做的任何修改不会更改原始副本的数据。 在使用值类型的集合(如 Dictionary<string, myStruct>)时,请务必记住这一点。 结构类型的变量直接包含了该结构的数据,而类类型的变量所包含的只是对相应数据的一个引用(被引用的数据称为“对象”)。但是结构仍可以通过ref和out参数引用方式传递给函数成员。 结构可用作可以为 null 的类型,因而可向其赋 null 值。
struct Simple { //public int x = 0; //结构中不能对字段进行初始化 public int x; public int y; //public Simple() //报错,结构中不能使用无参数的构造函数 //{ //} } class Csimple { public int x; public int y; } class Program { static void Main(string[] args) { Csimple cs1 = new Csimple(),cs2=null; //类的实例 Simple ss1 = new Simple(), ss2=new Simple(); //结构的实例 cs1.x = ss1.x = 5; cs1.y = ss1.y = 10; cs2 = cs1; ss2 = ss1; } }
类和变量的实例在赋值前后的栈区和堆区的比较
二 结构的装箱与拆箱
由于结构不是引用类型,当结构类型的值被转换为object 类型或由该结构实现的接口类型时,就会执行一次装箱操作。 反之,当 object 类型的值或接口类型的值被转换回结构类型时,会执行一次拆箱操作。 与对类类型进行的相同操作相比,主要区别在于: 装箱操作会把相关的结构值复制为已被装箱的实例,而拆箱则会从已被装箱的实例中复制出一个结构值。 因此,在装箱或拆箱操作后,对“箱”外的结构进行的更改不会影响已被装箱的结构。关注于装箱与拆箱的详解讲解,请看:http://www.cnblogs.com/wyh19941210/p/5847086.html
struct Program { static void Main(string[] args) { int i = 1;object o = i; //隐式装箱 i = 123; Console.WriteLine("i={0},o={1}",i,o); Console.Read(); } }//结果为:i=123,o=1
三 结构与构造函数
我们知道结构不能使用默认的构造函数,只能使用带参数的构造函数,当定义带参数的构造函数时,一定要完成结构所有字段的初始化,如果没有完成所有字段的初始化,编译时会发生错误。 结构可以使用静态构造函数吗? 可以,结构的静态构造函数与类的静态构造函数所遵循的规则大体相同。 结构的静态构造函数何时将触发呢? 结构的实例成员被引用,结构的静态成员被引用,结构显示声明的构造函数被调用。 但是创建结构类型的默认值不会触发静态构造函数。
为什么结构不能自定义无参数的构造函数? 结构类型的构造函数与类的构造函数类似,用来初始化结构的成员变量,但是struct不能包含显式默认构造函数, 因为编译器将自动提供一个构造函数,此构造函数将结构中的每个字段初始化为默认值表中显示的默认值。 然而,只有当结构用new实例化时,才会调用此默认构造函数。对值类型调用默认构造函数不是必需的。
struct A { static A() { Console.WriteLine("I am A."); } public void Fun() { } } class Program { static void Main(string[] args) { A a=new A(); a.Fun(); //结构的实例成员被引用Console.Read(); } } 结果为:I am A.
四 结构与继承
一个结构声明可以指定实现的接口列表,但是不能指定基类。 由于结构不支持类与结构的继承,所以结构成员的声明可访问性不能是 protected 或 protected internal。 结构中的函数成员不能是 abstract 或 virtual,因而 override 修饰符只适用于重写从 System.ValueType 继承的方法。 为在设计编程语言时将结构设计成无继承性? 其实类的继承是有相当的成本的 ——由于继承性,每个类需要用额外的数据空间来存储“继承图”来表示类的传承历史, 通俗地说来就是我们人类的家族家谱,里面存储着我们的祖宗十八代,只有这样我们才知道我们从哪里来的,而家谱肯定是需要额外的空间来存放的。 大家不要觉得这个存放“继承图”的空间很小,如果我们的程序需要用10000个点(Point)来存放游戏中的人物形体数据的话, 在一个场景中又有N个人,这个内存开销可不是小数目了。所以我们可以通过将点(Point)申明成 Struct而不是class来节约内存空间。
interface ITest { void Fun(int x,int y); } struct A:ITest { public void Fun(int x,int y) //隐式实现接口里的方法 { Console.WriteLine("x={0},y={1}", x, y); } } class Program { static void Main(string[] args) { A a; //结构的实例化可以不使用new a.Fun(1, 2); Console.Read(); } } // 结果为:x=1,y=2
五 结构与类的关系
相同点:
二者实际上都是创建对象的模板,都可以包含构造函数、常量、字段、方法、属性、索引器和运算符等成员
不同点:
(1)结构是值类型,类是引用类型在内存中结构是存储在栈取的值类型,类是存储在堆区的引用类型
(2)结构和类的构造函数不同,结构中不能包含无参数的构造函数,结构成员会自动初始化为它们默认值
(3)结构不能对实例字段成员进行初始化
(4)结构不能继承,就是一个结构不能从另外一个结构或者类中继承,而且不能作为一个类的继承