《C#本质论》读书笔记<一>基础语法、数据类型、异常处理

重读Csharp本质论一书,发现上面已经记录1500多条注释笔记,整理一下分享出来,里面会有对笔记的复制整理,也有一些新的思考。
PS:该书真是神书,除了基础语法以外,还讲解了“为什么”“如何做”这种问题,让读者知其然,也知其所以然。每个章节的内容深入浅出,覆盖面较广,有时还会提醒开发者,应该养成什么样的编码风格,无论是初学者还是有一定Csharp基础的开发者,都能有所收获。

带笔记版本的书https://download.csdn.net/download/lanazyit/12881254

基本类型篇

二进制浮点型

在这里插入图片描述
float和double都是二进制分数来表示的,无法准确的的表示十进制的小数。在这里插入图片描述
为什么float浮点型的精度是七位?
在C++里,float是用四个字节即三十二位二进制位来存储的。其中有1位符号位(正负),8位指数位和23位有效数字位 . 2^23=8388608,一共七位数字,这是23位能表达的最大数字

总结一下float容易出现问题的几种情况:

  • 赋值到双精度浮点数:float传递给double是一个分数,然后由double类型自己运算出结果,由于运算精确度的不同,产生的十进制结果自然也就不同
  • 进行数字比较:数字不准确,不要直接==操作
  • 超出范围: C#特殊处理,double和float超出范围会成为 Infinity
  • 除于0: C#特殊处理,double和float除以0会输出NaN
十进制浮点类型

decimal保证范围内的十进制小数都是精确的

字面量默认处理

值类型中如果没有特殊标注,小数默认处理为Double,这会造成一定的精度丢失。整数则会按照Int,uint,long,ulong的顺序进行适配,直到找到能存放字面量数值的类型,不会造成精度损失。
在这里插入图片描述
C#也提供了Round-Trip格式化的方式,规避小数点精度转到字符串有可能会丢失的问题。
在这里插入图片描述

如果有特殊标注,则按照以下规则进行处理
在这里插入图片描述

布尔值的大小

布尔值是8位,也就是一个字节
在这里插入图片描述

使用@阻止转义字符

在字符串的前面添加@符号可以,阻止后面的转义字符生效。如@“\n\tA”中的\n\t都会失效。但是@字符仍然允许""的转义,因为"会将字符串中断,需要做特殊处理在这里插入图片描述

字符串是不可变的!

书上讲的不全面,字符串不可变是因为以下两个方面

  • 性能方面 :
    • 字符串存在常量池中,相同字符串可以直接调用同一字面量
    • 字符串哈希值生成后,可以将哈希值缓存起来,不用重复生成
  • 安全性 :多平台编译时,不用考虑字符串编译过程中发生改变带来的不可控事故。
将Null赋给变量

在这里插入图片描述

引用类型存放地址

在这里插入图片描述很重要的一个图,表明了内存存放地址

引用类型的拷贝以及注意事项

在这里插入图片描述

可空修饰符

可控修饰符允许值类型与Null做判断,类似的用法还有delegate调用时的delegate?Invoke
在这里插入图片描述

类型溢出检测(checked)

使用checked包裹的代码块,会严格检查所有类型是否在范围内,如果溢出则会抛出System.OverflowException异常,unchecked则完全相反。
需要注意的是哪怕代码标记为unchecked,C#也会对特殊类型进行检测,比如Long不能转为Bool
在这里插入图片描述

强类型转换

字符串转换为值类型

在这里插入图片描述
小类型的互转(不能说值类型,因为string是引用类型)
在这里插入图片描述
防止转换出现异常可以使用TryParse,他在转换失败时,不抛出异常,而是返回 false
在这里插入图片描述

数组篇

数组使用流程表

在这里插入图片描述

声明数组,同时赋值

在这里插入图片描述

数组内存分配与初始值

使用New关键字会让运行时为数组分配空间,此时会将数组中的所有元素都初始化为default(type)具体内容参考图片

在这里插入图片描述
在这里插入图片描述

多维数组和锯齿数组

这两个概念容易混淆,所以也会经常放到一起比较。
其实很简单,
多维数组: 可以理解为一个矩形列表,要求同一个纬度的长度需要一致。我们需要在初始化时,明确指定所有维度的长度。
在这里插入图片描述

锯齿数组: 可以理解为由数组组成的数组,也就说第一维数组中存放了多若干个对其他数组的引用。声明时,只要指定第一维度的大小,但是要求每个新的纬度创建一个新的数组实例
在这里插入图片描述
数组长度属性Length
一维和多维数组返回元素总数,锯齿数组返回第一纬数组的元素长度。
在这里插入图片描述
如何获取多维数组的纬度数和某个维度的长度呢?

bool[,,] cells;
cells = new bool[2,3,3]
cells.GetLength(0); //获取第一维度的元素总数 取得2
cells.Rank;//获取纬度总数 取得3

如何复制数组?
参考这个前辈的博客
其中要注意,不要使用Clone方法,会有严重的装+拆箱GC

int[] copy=(int[])resArray.Clone();

数组内置的两个特殊方法BinarySearch和Clear
在这里插入图片描述
常见的数组错误
在这里插入图片描述
在这里插入图片描述

流程篇(分支,循环,特别是一些编码习惯)

  • 不要纠结于如何将代码写的更加精炼,而是应该注重可读性
    在这里插入图片描述

  • 提倡使用多使用小括号,特别是操作符优先级不能让人一目了然的时候
    在这里插入图片描述

  • 不要使用二进制浮点型进行精确地数值运算
    在这里插入图片描述

  • 不要轻易省略{},特别是if后面的{},容易因为代码缩进造成语义修改

  • 不要轻易使用位移操作符来代替2次方的乘除
    在这里插入图片描述

  • 尽量少使用goto,会让代码的可读性变得极差

  • 尽量不要使用using static 放止产生同名歧义,导致编译器报错

  • 移植C++代码时要检查递增递减的操作
    在这里插入图片描述

  • 不要对本地化的字符串直接进行+操作(主要原因应该是不同地域的语言的符号库不同)
    在这里插入图片描述

空结合操作符与Null条件操作符与三元运算符

这三种符号都表示在判断值为Null时的简化操作

  • 空结合操作符:a??b 如果a为非null,则a??b的结果为a;否则,结果为b。仅当a为null时,该操作才计算b。
  • Null条件操作符: A?.Invoke();null条件操作符在调用方法或属性之前检查操作数是否为null,如果为null,则不执行方法组。
  • 三元运算符 :条件表达式?表达式1:表达式2;a=3>4?3:4;输出为4。
位移操作符的补位

无符号数,在空位补0,有符号数,如果负数右移,在左侧补1(为了保证是负数),其余情况也是补0
在这里插入图片描述

常用的预处理指令在这里插入图片描述

类型篇(类)

使用别名

在这里插入图片描述在这里插入图片描述

Main方法中字符串数组参数的作用

之前一直好奇这个是干嘛用的,其实是提供程序启动时,传递Command(字符串)指令。这些参数会存储在内存中,非main方法可以通过图片中的方法取到这些参数在这里插入图片描述

调用栈和调用点

我更习惯称之为栈增长和栈坍缩
在这里插入图片描述

值传递与引用传递细解

可以这么理解,总底层来看,其实全部都算的上是值传递,因为都有一个复制操作,值类型是直接复制数据,引用类型是复制引用地址.
正因为复制的的是引用地址,所以修改引用类型参数的值时,也会改变引用类型原来的内容.
不过不可变引用类型是例外的,例如string类型,由于string类型不可修改,所以在参数时,只能替换一个新的string字符.这个时候改变的就是参数的引用地址,不会影响原字符串
另外按照我们命名的习惯, 传入的是引用类型的参数,就引用传递在这里插入图片描述

out参数

out参数与ref用法基本一致,out可以跳过初始化检查,另外C#会要求,out参数在方法return前必须要赋值。
在这里插入图片描述

参数数组(可变参数)
  • 只允许存在一个,并且要声明在最后面。
  • 数组声明后可以传入0或很多个同类型的元素,可以用逗号隔开,也可以显示使用数组调用。
    在这里插入图片描述在这里插入图片描述
可选参数

可选参数,就是提供了默认值的参数,可以在传递时将其忽略。如果忽略该参数,将自动声明为形参中的默认值。
但是要注意的是: 默认值必须是常量,可选参数必须在必须参数后面。
在这里插入图片描述
在这里插入图片描述

可选参数与可变参数可以同时存在

默认参数和数组参数同时存在时,数组参数要放到默认参数的后面,因为数组参数其实也是默认参数的一种(可以默认不传任何东西),而且数组参数明确规定了,必须要放到所有参数的最后

编译器选择方法的策略

要显示的调用方法,不要让阅读的人产生误解
在这里插入图片描述

可命名参数

类似Object-c的一种用法,使用不多,不过多描述。
在这里插入图片描述

递归

递归是算法中常用的分解思想的一种经典实现,易写宜读,但是效率一般,使用时要注意一定要有退出条件,不然会形成无限递归。

递归流程伪代码
在这里插入图片描述
递归的优劣势

  • 优势:易写,易读
  • 劣势:
  1. 参数频繁拷贝,特别是值类型参数,每次传递都要经历一次拷贝
  2. 调用栈有溢出风险,一般是调用深度太深,或形成了无限递归
  3. 函数调用会导致内存地址频繁变动
方法重载

一句话提炼:C#是依据方法名参数类型参数数量的不同来定义方法的唯一性,这些元素综合起来成为方法的签名。

重载的规范
在这里插入图片描述

异常处理

exception

所有的异常处理类,都要从System.Exception继承。
C#规定,catch异常时,要从"具体"到"不具体",也就是说要子类在前,父类在后。

常见的异常

在这里插入图片描述

常规catch块

没有指定异常类型的即为常规catch块,很少用且不推荐使用。
在这里插入图片描述
在这里插入图片描述

重新引发异常

当捕获一个无法妥善处理的异常时,可以尝试使用throw new exeption将其再次抛出异常,再次抛出的异常将会被后续catch中第一个兼容的catch块捕获。但是重新抛出的异常将会丢失原始的调用堆栈,该问题使用后缀的throw解决。具体代码如下
在这里插入图片描述

finally

finally一般用于异常触发后,做最后的清理,先记住一句话,finally终将执行
在这里插入图片描述
如果在Windows平台上使用,并有合适的调试器,finally也可以不执行!

不要在finally中触发异常
不要使用异常处理机制处理可以预期的错误操作

在这里插入图片描述

异常处理性能消耗

异常处理在throw时有一些性能消耗(其实也不会太高),在try块中不会有额外的性能消耗

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值