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

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
十进制浮点类型

字面量默认处理
值类型中如果没有特殊标注,小数默认处理为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前必须要赋值。

未完待续
智能推荐
C#图解教程读书笔记
注:会C语言学C#语法会容易很多,很多都是想通的,这本书讲的挺通俗易懂的,适合初学者 2.7.3 格式化字符串 2.7.5 格式化数字字符串 3.6 定义类型 3.7 栈和堆 程序运行中,他的数据必须存储在内存中。运行中的程序使用两个内存区域来存储数据:栈和堆 栈:后进先出 堆:可以以任意顺序存入和移除 3.8 值类型和引用类型 值类型直接存储其值,而引用类型存储对其值的引用。 4.3...
C和指针:读书笔记(一)
1.删除一段代码: #if #endif 2.数组参数是以引用的形式传递的(传值调用)。标量和常量是按值传递的(类似Pascal里的var) 3.\ddd ddd表示1-3个八进制数字,这个转义符表示的字符就是给定的八进制数值所代表的字符。 \xddd 与上类似,只是八进制转换成了十六进制数。 4.标准并没有规定长整型必须比短整型长,只是规定它不得比短整型短。 5.尽管设计char类型变量的目的是...
读书笔记: C# 7.0 in a nutshell (一、二章)
内容: 第一章: 内存管理 IL 框架 C# 7和6的特性 第二章: C#基础 编译 避免keyword冲突 类型 数字类型 Bool String Array 变量和参数Null Statement Namespace 第一章 介绍C#和 .NET Framework 内存管理 C# 依赖runtime来执行自动内存管理。比如.NET Framework中的 Common Language Ru...
《C#多线程编程实战》读书笔记
本文是一篇读书笔记,由《C#多线程编程实战》一书中的内容整理而来,主要梳理了.NET中多线程编程相关的知识脉络,从Thread、ThreadPool、Task、async/await、并发集合、Parallel、PLINQ到Rx及异步I/O等内容,均有所覆盖。为了帮助大家理解本文内容,首先给出博主在阅读该书过程中绘制的思维导图,大家可以根据个人需要针对性的查漏补缺。 线程基础 Tips1:暂停线程...
《CLR via C#》读书笔记-Attribute
什么是Attribute,Attribute的作用是什么 在代码中,有时需要携带一些额外的信息。可以限定代码的运行范围,决定代码是否对外可见;也可以是一些解释性的信息,保证程序的正常运行。而我们最常见、最常用的Attribute就是public、private、static之类,是不是有点小惊讶。而Attribute的作用就类似public、private一样,将附加信息与目标元素关联起来。因此更...
猜你喜欢
泛型——CLR via C# 读书笔记
1、具有泛型类型参数的类型仍然是类型,CLR同样会为它创建内部的类型对象。这一点适合引用类型(类),值类型(结构),接口类型和委托类型。然而具有泛型参数的类型称为开放类型,CLR禁止构造开放类型的任何实例。代码引用泛型类型时,可指定一组泛型类型实参。为所有类型参数都传递了实际的数据类型,类型就成为封闭类型。CLR允许构造封闭类型的实例。 如图所示: 2、泛型类型和继承 泛型类型仍然是类型,所以能从...
C#图解教程读书笔记(数组)
数组的类型 交错数组每个子数组的长度可以不同 数组是对象 数组是引用类型,因而数组的内容存储在堆上。 一维数组或矩形数组声明及其实例化 在访问数组时也是使用[,]的形式。 初始化数组 显示初始化 隐式初始化 交错数组 实例化交错数组 交错数组是独立数组的数组。不能在声明语句中初始化顶层数组之外的数组。...
《CLR via C#》读书笔记---11事件
核心归纳 核心赏析 CLR的事件模型~~ ① EventArgs是参数,定义通知其他类型时附带的信息 使用呢,继承EventArgs,并以EventArgs结尾即可 自定义一个~ ② 定义一个FCL已封装好的事件EventHandler,当在要调用执行通知行为的时候 我们会在通知的时候,看事件对象是否为null,会做一个判断,然后在LocationB这里执行通知 问题是如果在LocationB,外...
C#图解教程读书笔记(结构)
结构是值类型 分别声明一个类和一个结构,实例化后,结构直接保存在堆中。 所以当用一个对象给另一个对象赋值时会有不同: 在赋值后类类型是指向堆里同样的部分,所以cs2和cs1本质上是一样的。而结构类型则是复制值。类类型复制引用。 构造函数 可以调用隐士构造函数,也可以调用自己写的构造函数。 Simple类: 构造函数: struct本身派生自object的System.ValueType。不允许声明...
冒泡排序,及改进方式,性能优化400%>>>附图解加源码
首先源码附上,源码中带有注释,看不懂没关系,源码后面附带图解,最后附上代码效率提升图 源码如下: 方案一:其实实现很简单,两层循环,每次内层迭代出最大的一个值,将其放入数组最后一个位置,外层循环的末端便往前移一位。其原理如下图 方案一代码块: 方案二:优化改进 仔细观察上面的图,我们不难发现当迭代到图下这样的情况时,其实已经全排序好了,但是我们还是需要对它进行1,2,3,4,5,6的迭代,这些情况...
