C#学习笔记之结构体
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#中有关结构体的基础知识,虽然写了不少,可基本上没有涉及到更深,像一些更底层的东西都没有说到。而且由于技术有限难免有所疏漏错误,如果您看出来请不吝指出,谢谢。
本文参考学习自以下地址,谢谢原作者们的贡献:
C#学习笔记之结构体的更多相关文章
- contiki学习笔记---process结构体
process,字面意义,进程,看看它的结构 struct process { struct process *next; #if PROCESS_CONF_NO_PROCESS_NAMES #def ...
- 《PHP7底层设计与源码实现》学习笔记2——结构体对齐
书里给了一段代码,假如有个结构体如下: struct test { char a; int b; long c; void* d; int e; cha ...
- c语言学习笔记之结构体存储
今天讲讲结构体存储问题 首先,结构体简单说是对不同类型的封装,一开始我们可能会想结构体在内存中的存储的大小是直接元素的和 例如 我们可能会觉得是 结构体大小=int(4个字节)+ short(2个字节 ...
- C语言学习笔记--枚举&结构体
枚举 枚举是一种用户定义的数据类型,它用关键字enum以如下语法格式来声明: enum 枚举类型名字 {名字0,名字1,...,名字n}: 枚举类型名字通常并不真的使用,要用的是大括号里面的名字,因为 ...
- 【C#学习笔记】结构体使用
using System; namespace ConsoleApplication { struct _st { public string name; public int age; } clas ...
- Go学习笔记07-结构体与方法
Go学习笔记07-结构体与方法 Go语言 面向对象 结构的定义与创建 面向对象 Go语言只支持封装,不支持继承和多态. Go语言中只有struct,即结构体:没有class. 结构的定义与创建 pac ...
- C语言学习笔记10-结构体、枚举、联合体
C语言学习笔记10-结构体.枚举.联合体 待传
- iOS 阶段学习第十天笔记(结构体)
iOS学习(C语言)知识点整理 一.数据结构 1)概念:数据结构是指计算机程序中所操作的对象——数据以及数据元素之间的相互关系和运算. 2)结构体必须有struct 关键字修饰. 实例代码: stru ...
- ucos实时操作系统学习笔记——内核结构和任务创建
对于ucos实时操作系统,邵贝贝的那本书已经写得很详细了,我因为之前不深的研究过ucos,所以在这里做一个笔记,写一些个人对该操作系统的理解,仅仅是个人理解,如果有人看到这边随笔有不对的地方,望给我指 ...
随机推荐
- Linux(Centos、Debian)之安装Java JDK及注意事项(转)
--转自:http://www.cnblogs.com/hanyinglong/p/5025635.html 说明:本人是以Debian 操作系统来进行安装的,这篇文章有很大帮助,才学Linux对有些 ...
- 转载:CURL常用命令
转载网址:http://www.cnblogs.com/gbyukg/p/3326825.html 下载单个文件,默认将输出打印到标准输出中(STDOUT)中 curl http://www.cent ...
- Linux定时任务crontab命令使用详解
1.crontab功能介绍: crontab的功能是在一定的时间间隔内定时执行一些命令. 2.crontab参数详解: 1 crontab -u //设定某个用户的cron服务,一般root用户在执行 ...
- JS之arguments属性解读函数传参?
Arguments 该对象代表正在执行的函数 和 调用他的函数的参数. arguments属性:为当前执行中的 Function 对象返回 arguments 对象 和 参数. [function.] ...
- 魔法方法:构造和析构 - 零基础入门学习Python041
魔法方法:构造和析构 让编程改变世界 Change the world by program 构造和析构 什么是魔法方法呢?我们来系统总结下: - 魔法方法总是被双下划线包围,例如__init__ - ...
- 求奇数的乘积 AC 杭电
求奇数的乘积 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Sub ...
- jQuery CSS3 照片墙
<html> <head> <style type="text/css"> .picture-wall-container{ position: ...
- 基于.NET MVC的高性能IOC插件化架构(二)之插件加载原理
上一篇博文简单介绍了下插件化的代码组成部分:http://www.cnblogs.com/gengzhe/p/4390932.html,源码地址:https://github.com/luohuazh ...
- iOS推送 再备
这是一篇编译的文章,内容均出自Parse.com的iOS开发教程,同时作者还提供了视频讲解.本文将带领开发者一步一步向着iOS推送通知的深处探寻,掌握如何配置iOS推送通知的奥义. 介绍一点点背景资料 ...
- iOS网络编程-ASIHTTPRequest框架同步请求-备用
在ASIHTTPRequest框架中与HTTP请求相关的类有:ASIHTTPRequest和ASIFormDataRequest,其中最常用的是ASIHTTPRequest,ASIFormDataRe ...