C#之结构
本文意在巩固基础知识,并不是对其进行深入剖析,还望理解。
本文为原创文,难免会有一些小得瑕疵,敬请谅解。
所有示例均是博主测试过的,如有转载请标明出处,谢谢。
结构是使用 struct 关键字定义的,与类相似,都表示可以包含数据成员和函数成员的数据结构。
一般情况下,我们很少使用结构,而且很多人也并不建议使用结构,但作为.NET Framework 一般型別系统中的一个基本架构,还是有必要了解一下的。
结构的特征:
结构是一种值类型,并且不需要堆分配。
结构的实例化可以不使用 new 运算符。
在结构声明中,除非字段被声明为 const 或 static,否则无法初始化。
结构类型永远不是抽象的,并且始终是隐式密封的,因此在结构声明中不允许使用abstract和sealed修饰符。
结构不能声明默认构造函数(没有参数的构造函数)或析构函数,但可以声明带参数的构造函数。
结构可以实现接口,但不能从另一个结构或类继承,而且不能作为一个类的基类,所有结构都直接继承自 System.ValueType,后者继承自 System.Object。
结构在赋值时进行复制。 将结构赋值给新变量时,将复制所有数据,并且对新副本所做的任何修改不会更改原始副本的数据。 在使用值类型的集合(如 Dictionary<string, myStruct>)时,请务必记住这一点。
结构类型的变量直接包含了该结构的数据,而类类型的变量所包含的只是对相应数据的一个引用(被引用的数据称为“对象”)。但是结构仍可以通过ref和out参数引用方式传递给函数成员。
结构可用作可以为 null 的类型,因而可向其赋 null 值。
struct A
{
public int x; //不能直接对其进行赋值
public int y;
public static string str = null; //静态变量可以初始化
public A(int x,int y) //带参数的构造函数
{
this.x = x;
this.y = y;
Console.WriteLine("x={0},y={1},str={2}", x, y,str);
}
}
class Program
{
static void Main(string[] args)
{
A a =new A(1,2);
A a1 = a;
a.x =10;
Console.WriteLine("a1.x={0}",a1.x);
Console.Read();
}
}
结果为:x=1,y=2,str=
a1.x=1
此时a1.x值为1是因为,将a赋值给a1是对值进行复制,因此,a1不会受到a.x赋值得改变而改变。
但如果A是类,这时a和a1里的x引用的是同一个地址,则a1.x的值会输出10。
结构的装箱与拆箱
我们知道,一个类类型的值可以转换为object类型或由该类实现的接口类型,这只需在编译时把对应的引用当作另一个类型处理即可。
与此类似,一个object 类型的值或者接口类型的值也可以被转换回类类型而不必更改相应的引用。当然,在这种情况下,需要进行运行时类型检查。
由于结构不是引用类型,上述操作对结构类型是以不同的方式实现的。
当结构类型的值被转换为object 类型或由该结构实现的接口类型时,就会执行一次装箱操作。
反之,当 object 类型的值或接口类型的值被转换回结构类型时,会执行一次拆箱操作。
与对类类型进行的相同操作相比,主要区别在于:
装箱操作会把相关的结构值复制为已被装箱的实例,而拆箱则会从已被装箱的实例中复制出一个结构值。
因此,在装箱或拆箱操作后,对“箱”外的结构进行的更改不会影响已被装箱的结构。
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
什么情况下结构的实例化可以不使用new?
当结构中没有参数时,结构的实例化可以不使用new;
当结构中有参数时,必须对结构中所有参数进行初始化后,才能不使用new对结构进行实例化。
什么时候使用结构?
结构体适合一些小型数据结构,这些数据结构包含的数据以创建结构后不修改的数据为主;
例如:struct类型适于表示Point、Rectangle和Color等轻量对象。
尽管可以将一个点表示为类,但在某些情况下,使用结构更有效。
如果声明一个10000个Point对象组成的数组,为了引用每个对象,则需分配更多内存;这种情况下,使用结构可以节约资源。
定义的时候不会用到面向对象的一些特性;
结构体在不发生装箱拆箱的情况下性能比类类型是高很多的.