前言

接下来讲讲预定义数据类型。关于数据类型,其实是非常值得透彻研究的。

01

预定义数据类型

值类型和引用类型

C#将把数据类型分为两种,值类型和引用类型,值类型存储在堆栈上,引用类型存储在托管堆上。因此,对于值类型,如果:

Int a = 1;

Int b = a;

那么内存中就有两份的值1。

而对应引用类型,如果:

User userA = new User();

User userB = userA;

那么内存中只有一份User对象,userA和userB都指向它。

需要提到一句的是,结构(struct)是值类型,虽然我从来没有用到,但在需要极致优化性能的时候,可能是用的上的。

这基本上是大家都知道的。但在实际使用中,这种所谓的引用关系会比较隐秘,造成我们使用引用类型上的一些问题,所以我们需要更深入的讲一讲。

CTS类型(Common Type System)

CTS类型是.Net Framework的类型,而不是C#的类型,即我们之前讲到,CLR也是高度对象化的。在CLR层面规定了通用的类型,这样不同的开发语言比如C#,VB.Net才能良好的融合。比如Int32,Int64就是.Net框架的结构。我们定义一个Int和long既可以写为:

int val = 10;

long val2 1000;

也可以写为:

Int32 val = 10;

Int64 val2 = 1000;

当然我们实际使用中不会像后者那么用。

我们还可以发现,Int32,Int64是结构(struct),是值类型,所以这种类型通用化对.Net框架的性能的影响极小。

预定义的值类型

预定义的值类型有:

整数:

浮点数:

基本类型在赋值时,可以使用后缀(可大写或者小写)明确指定类型,比如:

Long val1 = 10L;

float val2 = 12.3F;

对于整数类型,如果不明确指定类型,系统会默认为int,而对于浮点数类型,不明确指定时系统默认为double。

我曾经很疑惑的是,既然这么说,是不是在初始化的时候一定要这么指定后缀呢?毕竟初始化基本类型是比较常见的操作。而我现在认为,非特殊情况下,基本上是不需要的。事实也是如此,我们很少见到有这么写的。比如对于:

Long val1 = 10L; 和 Long val1 = 10;

效果是一样的。因为val1定义为long,作为int的10会自动向上转型为long,不会有精度损失。而对于:

float val2 = 12.3F; 和float val2 = 12.3; 有没有区别呢?是有的,因为后面这句赋值语句有错误,不能通过编译,因为作为double的12.3转化为float,是有精度损失的,所以要么你强制转换,要么你加F。

所以我们会发现,你写一个对基础类型的赋值语句,你能写出来,且编译器没提示错误,就是没问题的;不能写的,编译器会提醒你。编译器已经足够智能,不需要担心要不要加后缀的问题。

Decimal类型:

Decimal是个很重要的类型,我在实际项目中,从来没有用过float和double,因为它们有精度问题,我都是要么整数(用于数量),要么Decimal(用于价格,金额等)。.Net把它称为“用于财务计算的专用类型”。我们对比Java会发现,Java的高精度计算类型是一个叫做BigDecimal的普通类,它的初始化要用比如new BigDecimal(10)这种类创建的方式来实现。而在C#中将其地位提升为预定义类型。但同时,书中也强调,Decimal仍然不是“基本类型”,我们可以认为,它的本质和Java的BigDecimal是一样的,只是将其“模拟”为基本类型,这样使用更便捷,但它的计算实际上仍然会有性能损失。

Bool类型:

字符类型:

字符类型,有多种表示方式:

char val1 = 'A';        //字面量

char val2 = '\u0041';   //Unicode

char val3 = (char)65;   //int转换

char val4 = '\x0041';   //16进制

字符常用的还有用“转义符”(反斜杠\)表示的特殊字符,比如换行符\n,回车符\r等。

预定义的引用类型

预定义引用类型的概念太重要了,因为它是.Net整个类结构的基础。

上图的每个字都值得分析。我们注意到,object类型被叫做“根类型”,它是包括值类型的所有其他类型的父类型。为什么值类型也是?因为值对象可以被“装箱”并放入托管堆而成为引用对象,而装箱又导致拆箱,这是非常重要的概念,对这个概念理解的不好,会写出意想不到的复杂代码。

而String类型的概念是:Unicode字符串,也很重要,这说明什么,说明字符乱码问题得到了彻底解决,一个英文和一个中文字符长度相同,等等。String类型是个非常特殊又奇怪的类型,它是引用类型,但它的行为又像值类型,这是特意设计的,因为字符串的使用太普遍了,而值类型的概念比引用类型使用起来更直观和简单。它的几个特点:

  1. String对象是引用类型,它被分配在堆上,而不是栈上
  2. 因此,当把一个字符串变量赋予另一个字符串时,会得到对内存中同一个字符串的两个引用
  3. 字符串是不可改变的。修改其中一个字符串,就会创建一个全新的String对象,而另一个字符串不发生任何变化

有了以上特性,我们可以看到值类型的行为了:当赋予一个不同的字符串值,系统总给你new一个字符串新值;而当你赋值已有的某个字符串时,系统从堆中给你找出来赋值给你。这样既保证了值类型的行为,也优化了引用类型的性能,是一种平衡的好方案。当然在某些场景下,String的这种特性也会导致性能问题,比如在for循环中拼接一个字符串,这个时候简单的用赋值语句改变String时,就会导致一直创建新的字符串,性能大降,所以这个时候我们常常会用StringBuilder来构造字符串。

字符串中,常碰到转义符的问题,字符串中的\将被理解为转义符,怎么让C#不将其理解为转移符呢?“将转义符再转义”,即再加\,比如一个目录路径表示为:

string path = “C:\\windows\\Temp”;

我们可以简化这种写法为:

string path = @“C:\windows\Temp”;

@不仅是用于描述路径,还用于其他多种场景,这会让人混淆,@有没有可明确描述的效用?有的,它的功能可通用性的描述为:在这个@后的所有字符都看作是其原来的含义。比如上面的路径例子,原来的含义就是\就是路径的表述,而不是转义符的意思。还有比如@用在字符串换行中:

string path = @“华为是

中国的骄傲”;

当没有@时,字符串在输入中换行会导致编译错误,因为回车换行在语言规范中有特殊含义,加了@表示告诉编译器,“我这字符串中的换行符就是原本的换行的意思,你不用特殊解释”。

其他所有用@的场景,都可以如此解释,这是一种可通用的解释。

预定义类型内容非常长,就先讲到这里,下一篇我们讲流控制。

觉得文章有意义的话,请动动手指,分享给朋友一起来共同学习进步。

欢迎关注本人微信公众号,更及时的关注最新文章(每周三篇原创文章,以及多篇专题文章):

附文:

C#的两种类据类型:值类型和引用类型

C# String与StringBuilder

上一篇:解读经典《C#高级编程》第七版 Page20-32.核心C#.Chapter2

解读经典《C#高级编程》第七版 Page32-38.核心C#.Chapter2的更多相关文章

  1. c#高级编程第七版 学习笔记 第一章 .NET体系结构

    第一章      .NET体系结构 本章内容: 编译和运行面向.NET的代码 Microsoft中间语言(Microsoft Intermediate Language,MSIL或简称IL)的优点 值 ...

  2. c#高级编程第七版 学习笔记 第二章 核心c#

    第二章 核心C# 本章内容: 声明变量 变量的初始化和作用域 C#的预定义数据类型 在c#程序中使用条件语句.循环和跳转语句执行流 枚举 名称空间 Main()方法 基本的命令行c#编译器选项 使用S ...

  3. c#高级编程第七版 学习笔记 第三章 对象和类型

    第三章 对象和类型 本章的内容: 类和结构的区别 类成员 按值和按引用传送参数 方法重载 构造函数和静态构造函数 只读字段 部分类 静态类 Object类,其他类型都从该类派生而来 3.1 类和结构 ...

  4. C#高级编程第9版 第二章 核心C# 读后笔记

    System命名空间包含了最常用的.NET类型.对应前面第一章的.NET基类.可以这样理解:.NET类提供了大部分的功能,而C#语言本身是提供了规则. pseudo-code,哈哈,秀逗code.伪代 ...

  5. ASP.NET MVC 4高级编程(第4版)

    <ASP.NET MVC 4高级编程(第4版)> 基本信息 作者: (美)Jon Galloway    Phil Haack    Brad Wilson    K. Scott All ...

  6. 《UNIX环境高级编程(第3版)》

    <UNIX环境高级编程(第3版)> 基本信息 原书名:Advanced Programming in the UNIX Environment (3rd Edition) (Addison ...

  7. 【转】apue《UNIX环境高级编程第三版》第一章答案详解

    原文网址:http://blog.csdn.net/hubbybob1/article/details/40859835 大家好,从这周开始学习apue<UNIX环境高级编程第三版>,在此 ...

  8. Linux - Unix环境高级编程(第三版) 代码编译

    Unix环境高级编程(第三版) 代码编译 本文地址:http://blog.csdn.net/caroline_wendy 时间:2014.10.2 1. 下载代码:http://www.apuebo ...

  9. Unix环境高级编程第三版中实例代码如何在自己的linux上运行的问题

    学习Linux已经有2个月了,最近被期末考试把进度耽误了,前几天把Unix环境高级编程看了两章,感觉对Linux的整体有了一些思路,今天尝试着对第一章涉及到的一个简单的交互式shell编译运行一下,结 ...

  10. C#高级编程笔记之第二章:核心C#

    变量的初始化和作用域 C#的预定义数据类型 流控制 枚举 名称空间 预处理命令 C#编程的推荐规则和约定 变量的初始化和作用域 初始化 C#有两个方法可以一确保变量在使用前进行了初始化: 变量是字段, ...

随机推荐

  1. CentOS 7 rpm安装jdk

    RPM 安装jdk1.8.0_111 ,查询系统自带的jdk rpm -qa | grep java 查询结果如下: [root@bogon ~]# rpm -qa | grep java javap ...

  2. [译]使用explain API摆脱ElasticSearch集群RED苦恼(转)

    "哔...哔...哗",PagerDuty的报警通知又来了. 可能是因为你又遭遇了节点宕机, 或者服务器机架不可用, 或者整个ElasticSearch集群重启了. 不管哪种情况, ...

  3. STM32CubeMX HAL库串口+DMA数据发送不定长度数据接收

    参考资料:1.ST HAL库官网资料 2.https://blog.csdn.net/u014470361/article/details/79206352#comments 一.STM32CubeM ...

  4. redis_简单动态字符串

    在redis中,C字符串(以'\0'结尾的字符数组)只用在一些无需对字符串值进行修改的地方,比如打印日志.其他情况,redis使用SDS - SimpleDynamicString 简单动态字符串,来 ...

  5. c#项目减少源代码大小

    这次的代码缩减主要通过了这几个方面 1.bin和obj文件的删除(以前真的不知道,只是通过右键属性发现这些文件太大,然后上网搜索才知道,这些文件在源代码备份的时候是建议删掉的) 删掉的好处: 1.减少 ...

  6. Codeforces Round #425 (Div. 2)

    A 题意:给你n根棍子,两个人每次拿m根你,你先拿,如果该谁拿的时候棍子数<m,这人就输,对手就赢,问你第一个拿的人能赢吗 代码: #include<stdio.h>#define ...

  7. MFC对话框和属性表

    对话框主要有两类,分为模式和无模式,它们的功能都被封装在了CDialog中.与对话框相近的是属性表,属性表实际上是具有制表页(TAB 页)的对话框,将对话框中控件更好的组织在一起.借助Cpropert ...

  8. 强大的jQGrid的傻瓜式使用方法。以及一些注意事项,备有相应的引入文件。

    在介绍我的使用前,先按照国际惯例,列上网址http://blog.mn886.net/jqGrid/ 里面第一项就有相应的demo. 好,进入正题: 在学习到node.js的时候,需要使用到jQGri ...

  9. 前端开发掌握nginx常用功能之server&location匹配规则

    nginx主要是公司运维同学必须掌握的知识,涉及到反向代理.负载均衡等服务器配置.前端开发尤其是纯前端开发来说对nginx接触的并不多,但是在一些情况下,nginx还是需要前端自己来搞:例如我们公司的 ...

  10. Linux基本命令-ls

    ls 作用:显示目标列表,在Linux中是使用率较高的命令.ls命令的输出信息可以进行彩色加亮显示,以分区不同类型的文件. 参数: -a:显示所有档案及目录(ls内定将档案名或目录名称为“.”的视为影 ...