1.概述

结构是一种与类相似的数据类型,不过它较类更为轻量,一般适用于表示类似Point、Rectangle、Color的对象。基本上结构能办到的类全都能办到,但在某些情况下使用结构更为合适,后面会有提到。

结构具有以下特点:

  • 结构可以实现接口。
  • 结构可以声明带参数的构造函数。
  • 结构不能声明默认构造函数(没有参数的构造函数)或析构函数。
  • 结构是值类型,而类是引用类型。
  • 实例化结构体时可以不使用new运算符。
  • 结构类型是不可抽象、隐式密封的,故不能使用abstract和sealed修饰符。
  • 在结构中声明字段时,字段无法被初始化,除非字段加上关键字const或static。
  • 一个结构不能从另一个结构或类继承,而且不能作为一个类的基。所有结构都直接继承自 System.ValueType,后者继承自 System.Object。
  • 结构在赋值时进行复制。将结构赋值给新变量时,将复制所有数据,并且对新副本所做的任何修改不会更改原始副本的数据。 不过结构仍然可以使用ref和out参数引用的方式传值给函数成员。另,在使用值类型的集合(如 Dictionary<string, myStruct>)时,请务必记住这一点。

2.结构体声明

    //结构体的声明
public struct Rectangle
{
public double x,y;
public static double z = 5;//结构体里的字段只有加上static或者const关键字才能被初始化 //结构体里的构造函数必须是带参数的
public Rectangle(double x1, double y1)
{
x= x1;
y= y1;
Console.WriteLine(x.ToString()+" "+y.ToString());
}
} class Prgram1
{
static void Main(string[] args)
{
//结构体的初始化
Rectangle rect1;
Rectangle rect2 = new Rectangle();
Rectangle rect3 = new Rectangle(5, 5); //输出各个的值
Console.Write("rect1:");
Console.WriteLine("x = {0}, y = {1}", rect1.x, rect1.y); Console.Write("rect2:");
Console.WriteLine("x = {0}, y = {1}", rect2.x, rect2.y); Console.Write("rect3:");
Console.WriteLine("x = {0}, y = {1}", rect3.x, rect3.y); }
} /* 输出:
rect1: x = 0, y = 0
rect2: x = 0, y = 0
rect3: x = 5, y = 5
*/

示例1

3.结构体赋值

由于结构体是值类型,故将一个结构体赋给新变量时,同时将复制它的所有数据,但是对新副本所做的修改不会影响原结构体。简单的说就是赋值时只复制值不复制地址。示例如下:

    class Prgram2
{
static void Main(string[] args)
{
Rectangle rect = new Rectangle(10, 10);
Rectangle rect1 = rect;
rect1.width = 5;
rect1.length = 5; Console.Write("rect:");
Console.WriteLine("x={0},y={1}",rect.length,rect.width); Console.Write("rect1:");
Console.WriteLine("x={0},y={1}", rect1.length, rect1.width);
}
}
/* 输出:
rect: x = 10, y = 10
rect1: x = 5, y = 5
*/

示例2

但在这里我有过小小的疑问,MSDN中在说到结构体的赋值时提到:“结构可用作可以为 null 的类型,因而可向其赋 null 值”,这句话有点饶,乍一看就想一个值类型怎么能赋空值呢。后来仔细看了一下这句话提到的“可以为null的类型”,才发现它指的是Nullable结构即可空类型,这下明白了。对于值类型如果要赋空值,可以使用Nullable结构。用法就是Nullable<Rectangle>或者更方便的Rectangle?,关于可空类型园子里讲的很多,这里就不多说了。

4.结构体的装箱拆箱

大家都知道,装箱就是值类型到引用类型转变的过程,拆箱就是引用类型到值类型转变的过程。对于结构体来说当然也是这样,当一个结构体转换成Object或者它所继承的接口类型时,就是装箱,反之则是拆箱。值得一提的是,当我们把结构体转换后,再次改变结构体的值,转换成的引用对象的值是不变的,因为转变过后该引用对象就是不再是原来的值对象了。示例如下:

    interface IPolygon
{
int EdgeNum { get; set; }
} public struct Rectangle : IPolygon
{
private int edgenum;
public int EdgeNum
{
get { return edgenum; }
set { edgenum = value; }
}
} class Prgram3
{
static void Main(string[] args)
{
Rectangle rect = new Rectangle();
rect.EdgeNum = 4;
Console.WriteLine("rect.EdgeNum={0}", rect.EdgeNum);//4 //隐式装箱为接口类型
IPolygon polygon = rect as IPolygon;
polygon.EdgeNum = 8;
Console.WriteLine("Changed interface.");
Console.WriteLine("polygon.EdgeNum={0},rect.EdgeNum={1}",polygon.EdgeNum,rect.EdgeNum);//8,4 //改变结构体的值再输出
rect.EdgeNum = 5;
Console.WriteLine("Changed Struct.");
Console.WriteLine("polygon.EdgeNum={0},rect.EdgeNum={1}", polygon.EdgeNum, rect.EdgeNum);//8,5
}
}

示例3

5.结构体的构造函数

关于结构体的构造函数,有以下几点需要知道:

当结构包含引用类型作为成员时,必须显式调用该成员的默认构造函数,否则该成员将保持未赋值状态且该结构不可用。

当我们声明一个结构体时,编译器会自动为其生成一个隐含的构造函数,该构造函数会将结构体中的每个字段初始化为默认值表中显示的默认值,而这个构造函数是不允许被替换的,故结构体不能自定义无参的构造函数。

当结构体中含有私有成员或以其他方式设置的不可访问成员时,这些成员只能在有参构造函数中进行初始化。并且当定义有参构造函数时,一定要完成所有字段的初始化,如果没有完成所有字段的初始化,编译时会发生错误。

结构体可以使用静态的构造函数,其使用方法与类的静态构造函数相类似。当结构体的第一个实例成员或静态成员或显示声明的构造函数被调用前,静态构造函数会被调用,且在整个过程中只被调用一次。

    public struct Rectangle
{
public double x,y;
public static int edgeNum; static Rectangle()
{
Console.WriteLine("The static constructor invoked");
} public void Test()
{
Console.WriteLine("The Test function invoked");
} public Rectangle(double x1,double y1)
{
x = x1;
y = y1;
}
} class Prgram4
{
static void Main(string[] args)
{
Rectangle.edgeNum = 4;
Console.WriteLine("Rectangle.edgeNum = {0}", Rectangle.edgeNum); Rectangle rect = new Rectangle(5, 5);
Console.WriteLine("rect.x={0},rect.y={0}", rect.x, rect.y); Rectangle rect1=new Rectangle ();
rect1.Test(); Console.Read();
}
} //上面三种都可以触发静态构造函数,但只会出现一次"The static constructor invoked"

示例4

6.结构体的多态和继承

结构体并不像类那样存在继承,它是隐式密封的,它可以实现接口但不能指定基类,并且由于这个特性它的成员的声明不能加protected和internal关键字。

结构体的成员不能是abstract或者virtual,因而voerride修饰符只适用于重写从System.ValueType继承的方法。

关于结构体为什么是不可继承的,下面这段话解释的很好:

为什么设计编程语言时将结构设计成无继承性?­
其实类的继承是有相当的成本的 ——由于继承性,每个类需要用额外的数据空间来存储“继承图”来表示类的传承历史,
通俗地说来就是我们人类的家族家谱,里面存储着我们的祖宗十八代,只有这样我们才知道我们从哪里来的,而家谱肯定是需要额外的空间来存放的。
大家不要觉得这个存放“继承图”的空间很小,如果我们的程序需要用10000个点(Point)来存放游戏中的人物形体数据的话,
在一个场景中又有N个人,这个内存开销可不是小数目了。所以我们可以通过将点(Point)申明成 Struct而不是class来节约内存空间。

7.结构体的适用场合

结构体适合一些小的数据结构,如Point、Rectangle、Color等,如果它们使用类类型的话,为了引用每个对象,则需分配更多内存;这种情况下,使用结构可以节约资源。或者如果确定数据结构不会用到一些面向对象的特性,那么结构体与类相比是个更好的选择。

--------------------------------------------分----------------割--------------线---------------------------------------------------

本文算是梳理了C#中有关结构体的基础知识,虽然写了不少,可基本上没有涉及到更深,像一些更底层的东西都没有说到。而且由于技术有限难免有所疏漏错误,如果您看出来请不吝指出,谢谢。

本文参考学习自以下地址,谢谢原作者们的贡献:

MSDN-使用结构

C#之结构

C# 结构体 struct

C# - 结构体转换为接口引用时隐式装箱的影响

C#学习笔记之结构体的更多相关文章

  1. contiki学习笔记---process结构体

    process,字面意义,进程,看看它的结构 struct process { struct process *next; #if PROCESS_CONF_NO_PROCESS_NAMES #def ...

  2. 《PHP7底层设计与源码实现》学习笔记2——结构体对齐

    书里给了一段代码,假如有个结构体如下: struct test {     char a;     int b;     long c;     void* d;     int e;     cha ...

  3. c语言学习笔记之结构体存储

    今天讲讲结构体存储问题 首先,结构体简单说是对不同类型的封装,一开始我们可能会想结构体在内存中的存储的大小是直接元素的和 例如 我们可能会觉得是 结构体大小=int(4个字节)+ short(2个字节 ...

  4. C语言学习笔记--枚举&结构体

    枚举 枚举是一种用户定义的数据类型,它用关键字enum以如下语法格式来声明: enum 枚举类型名字 {名字0,名字1,...,名字n}: 枚举类型名字通常并不真的使用,要用的是大括号里面的名字,因为 ...

  5. 【C#学习笔记】结构体使用

    using System; namespace ConsoleApplication { struct _st { public string name; public int age; } clas ...

  6. Go学习笔记07-结构体与方法

    Go学习笔记07-结构体与方法 Go语言 面向对象 结构的定义与创建 面向对象 Go语言只支持封装,不支持继承和多态. Go语言中只有struct,即结构体:没有class. 结构的定义与创建 pac ...

  7. C语言学习笔记10-结构体、枚举、联合体

    C语言学习笔记10-结构体.枚举.联合体    待传

  8. iOS 阶段学习第十天笔记(结构体)

    iOS学习(C语言)知识点整理 一.数据结构 1)概念:数据结构是指计算机程序中所操作的对象——数据以及数据元素之间的相互关系和运算. 2)结构体必须有struct 关键字修饰. 实例代码: stru ...

  9. ucos实时操作系统学习笔记——内核结构和任务创建

    对于ucos实时操作系统,邵贝贝的那本书已经写得很详细了,我因为之前不深的研究过ucos,所以在这里做一个笔记,写一些个人对该操作系统的理解,仅仅是个人理解,如果有人看到这边随笔有不对的地方,望给我指 ...

随机推荐

  1. winform中相对路径和绝对路径的获取

    例如: Path.GetFullPath(Path.Combin(@"C:\a\b\c","..\b.text")); ..代表上级目录 .代表当前目录 结果: ...

  2. powerpoint无法输入中文怎么办|ppt文本框无法输入中文解决办法

    powerpoint文本框无法输入中文的情况不知大家是否遇到过呢?反正小编是遇到过这样的情况的,简直是急煞人也!那么powerpoint无法输入中文时应该怎么办呢?本节内容中小编就为大家带来ppt文本 ...

  3. 2017-1-9css

    2017-1-9css css border-image详解 http://www.360doc.com/content/14/1016/13/2792772_417403574.shtml 最简单的 ...

  4. IOS编程教程(八):在你的应用程序添加启动画面

    IOS编程教程(八):在你的应用程序添加启动画面   虽然你可能认为你需要编写闪屏的代码,苹果已经可以非常轻松地把它做在Xcode中.不需要任何编码.你只需要做的是设置一些配置. 什么是闪屏 对于那些 ...

  5. obj文件的连接问题以及tlib的基本用法

    1.基础研究 用tcc将程序编译为.obj文件. 这里也可以使用tcc -linclude run.c来将run.c文件编译成run.obj文件. 再用tcc对下面的程序进行编译链接,发现提示错误: ...

  6. 如何统一修改 Altium Designer 中的字符大小

    如下图 1 所示: Q1. Q2. C1. C2. R1 等等的字符你想统一修改他们的大小.原来是 Text Height( 100mil), Text Width( 12mil),想改成 Text ...

  7. Valve开源了Direct3D到OpenGL的转译层,方便开发者迁移游戏到Linux(面向游戏玩家的六款最佳 Linux 发行版)

    Valve开源了Direct3D到OpenGL的转译层,方便开发者迁移游戏到Linux:https://github.com/ValveSoftware/ToGL Valve SteamBox主机系统 ...

  8. Jquery 对象集合的迭代扩展forEach

    if (jQuery && !jQuery.fn.forEach) { $(function () { (function ($) { $.fn.extend({ forEach: f ...

  9. json与jsonp ajax

    今天在网上找了一下资料,发现这篇文章总结得好,果断转了:http://blog.csdn.net/superhosts/article/details/9057301

  10. js中()()问题

    var aa=function(){}(); var bb=(function(){})(); 今天被问到这个问题,这段js有撒区别. 总结一下,两个函数都是立即执行的意思.但是不同之处是执行的顺序, ...