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. Axure自动幻灯片制作

    1.打开axure7,开始. 2.拖一个占位符组件到布局上(当然也可以是矩形.图片之类的),大小270*170,作为幻灯片的第一张片子,双击写上“第一张片子”. 3.拖一个矩形,设置形状为椭圆,调整大 ...

  2. asp.net 字符帮助类 类型转换类

    /// <summary> /// 字符帮助类 /// </summary> public class StringHelper { private static readon ...

  3. 自己动手开发编译器(五)miniSharp语言的词法分析器

    稍微说明一点,整型常量和上面的标识符的词法,在调用lex.DefineToken时都多传了一个参数.这个参数是可选的描述信息,如果不传会直接使用正则表达式的字符串形式.而标识符的正则表达式有4万多个字 ...

  4. Speed-BI报表按钮链接设置

    使用Speed-BI开发报表的时候,常常会需要增加一个链接按钮,点击按钮可跳转到其他报表或外部链接:那么我们可以通过新增‘仪表盘’--‘文本框’图表(如图1), <ignore_js_op> ...

  5. grunt serve Warning: Running "sass:server" (sass) task

    使用grunt serve运行时遇到一问题: y@y:ydkt$ grunt serve Running "serve" task Running "clean:serv ...

  6. Altium Designer 里面怎么画等长线

    (1)一般是将走线布完后,新建一个class. Design -> Classes 如上图添加完后可以点击close. (2)快捷键 T + R: 或者 点击Tools 下拉中的Interact ...

  7. C++代码覆盖率工具Coverage Validator

    市面上的C++代码覆盖率工具大都收费,Coverage Validator也不例外.Coverage Validator应该少有人听过,我也是在stackoverflow里听别人介绍的.所以下载了试用 ...

  8. QT程序制作deb包并安装在应用程序菜单

    制作原理:打包:将QT制作的源程序(没有编译的)用debian压缩打包(这里是用脚本对源程序再编译)安装:将deb包中的源程序解压(默认解压到根目录)到规定系统文件中并编译(postinst脚本)卸载 ...

  9. [LeetCode] 116. Populating Next Right Pointers in Each Node 解决思路

    Given a binary tree struct TreeLinkNode { TreeLinkNode *left; TreeLinkNode *right; TreeLinkNode *nex ...

  10. httpd与tomcat基于mod_jk整合

    搞定在前面述, httpd与tomcat整合方式 当前已知的有 ajp_proxy,mod_jk.so jk connecteor连接器下载地址 http://archive.apache.org/d ...