1.bug只与规则有关1.1.如果根本没有任何规则,那么bug也就不存在了1.2.公司也就可以不用拙劣的借口“哦,那只是一个feature”来做危机公关1.3.你不需要为规则写一份书面文档——你可以只让它存在于自己的脑海里1.4.Bug是衡量软件质量的基准参考2.类型有大用2.1.快速并不仅仅涉及代码编写速度,你还得算上代码维护的速度2.2.软件开发是一场马拉松,而不是短跑2.3.指定数据类型是在编程中防止数据编码出现冲突的最早防范措施之一2.4.类型让你尽早受挫,让你在代码中的隐患酿成大错之前,修复它们2.5.使用强类型2.5.1.类型检查可以算是对代码正确性的免费初审2.5.2.好好吃透底层类型系统会让你在成为资深开发者的路上走得更悠然2.5.3.类型可以帮助我们编写更安全、更快速运行、更容易维护的代码2.6.使用解释型编程语言编写代码的速度非常快,因为你并不需要真正地去声明变量的类型2.7.有效性证明2.7.1.有效性证明算是预定义类型较少为人所知的一种好处2.7.2.数据验证并不能给全部代码的有效性证明“打包票”2.7.3.类型可以承载有效性证明2.7.3.1.在构造时验证其有效性,这样一来就不可能包含无效值2.7.4.运算符重载,非必要不启用2.8.奥尔曼(Allman)样式2.8.1.每个花括号都在自己专属的行上2.8.2.官方推荐使用1TBS(一种真正的花括号样式),也就是改进的K&R样式2.8.2.1.其主张一个花括号与声明在同一行2.9.巧用框架2.9.1.一些由你自定义的、基于文本的值,比如URL、IP地址、文件名,甚至日期,这些都存储为字符串2.9.1.1.看看这些现成的类型2.9.2.IPAddress比string类型更合适存储IP地址,不仅因为它有验证功能,而且它对现已投入使用的IPv6的支持也非常不错2.9.3.TimeSpan2.9.3.1.它代表持续时间2.9.3.2.你没有理由用TimeSpan以外的任何东西来表示持续时间,即使你所调用的函数不接受TimeSpan作为参数2.9.4.总是尽量使用DateTimeOffset而不是DateTime2.9.4.1.它也很容易转换为DateTime2.9.4.2.可以使用算术运算符操作TimeSpan和DateTimeOffset,这要归功于运算符重载2.9.5.乔恩·斯基特(Jon Skeet)的Noda Time2.10.用类型防止打错字2.11.null的可与不可2.11.1.“十亿美元的错误”2.11.1.1.托尼·霍尔(Tony Hoare)2.11.1.1.1.null的发明者2.11.2.null,某些语言中写作nil,是一个值,它代表着没有值或程序员的冷漠2.11.3.由于具有0值的内存地址意味着内存中的无效区域,现代CPU可以捕获无效访问并将其转换为对人类友好的异常消息2.11.4.可空引用2.11.4.1.当所有引用类型都可以为null的时候,所有接受引用的函数都可以接收两个不同的值:有效引用和null2.11.5.is运算符不能被重载2.11.6.如果问题及早暴露,查看异常的栈跟踪时,会发现它非常干净,方便排查2.11.6.1.你能确切地知道是哪个参数导致函数运行失败2.11.7.当你没办法而非得使用空字符串时,不要使用 “”符号来表示空字符串2.11.7.1.很容易将其与一个带有单个空格的字符串(” “)混淆2.11.7.2.可以用String.Empty表示,利用现有类型2.11.8.C# 9.0中引入了一个新的结构,名叫记录类型(record type)2.11.8.1.创建一个不可变的类2.11.9.Maybe<T>已死,Nullable<T>永恒2.11.9.1.编译器既能检查又能优化nullable类型,比临时的实现更好2.11.9.2.你还可以从语言中得到运算符和模式匹配的语法支持2.11.10.null检查有助于你思考正在写的代码的作用,你会更加清楚地知道值到底是不是需要设定为可选的2.11.10.1.它会减少bug,让你成为更好的开发者2.12.免费的更好性能2.12.1.一个有效的IPv6字符串最多可以有65个字符2.12.2.IPv4地址的长度至少为7个字符2.12.3.基于字符串的存储将占用14~130字节,如果包含对象头,则占用30~160字节2.12.4.PAddress类型将IP地址存储为一系列字节,只使用20~44字节2.13.引用类型与值类型2.13.1.引用类型和值类型之间的区别主要在于类型在内存中的存储方式2.13.2.值类型的内容存储在调用栈中2.13.2.1.值类型之所以存在,是因为在某些情况下,它们在存储和性能方面都比引用类型效率更高2.13.2.1.1.对于值类型来说,运行时直接读取该值,从而实现更快的访问速度2.13.3.引用类型(reference type)存储在堆(heap)中2.13.3.1.只有在引用类型内容的时候才会将其存储到调用栈中2.13.3.2.引用也导致单层间接性(a single level of indirection)2.13.3.2.1.当你需要访问一个引用类型的字段时,.NET运行时必须先读取引用的值,然后转到引用所指向的地址,再从那里读取实际的值2.13.4.一个不可变、仅表示数值的类,行内人称为值类型(value type)2.13.5.结构在定义上与类(class)非常相似,但与类不同的是,它在任何地方都是通过值传递的2.13.6.引用(reference)类似于管理指针(managed pointer)2.13.6.1.指针是内存的地址2.13.7.虚拟内存(virtual memory)2.13.8.垃圾回收2.13.8.1.程序员需要注意他们的内存分配,并在完成内存分配后释放分配的内存2.13.8.2.不及时释放内存的话,应用程序的内存使用量会不断增加,这也称为内存泄漏2.13.8.3.引用计数(reference counting),是第一个被提出用来解决手动内存管理问题的方案2.13.8.3.1.是垃圾回收的一种原始形式,运行时将为每个分配的对象保留一个秘密计数器(secret counter),而不是将释放内存的主动权留给程序3.旅行推销员问题3.1.计算机科学中的一个基础主题3.2.Traveling Salesperson Problem,TSP3.3.NP完全问题(NP-complete)3.3.1.非确定性多项式时间完全问题(nondeterministic polynomial-time complete)3.3.2.NP是P(多项式时间)问题的超集,只能用“蛮力”解决3.3.3.意味着“我们几乎解决不了这个问题,但我们可以马上验证一个别人建议的解决方案”3.4.基于图灵机的编程语言是图灵完全的3.4.1.艾伦·图灵(Alan Turing)3.4.2.图灵机允许我们在软件开发方面有无限的可能性,但不利用图灵机就无法验证软件开发的正确性3.4.3.由于图灵机的性质,错误是不可避免的3.4.3.1.不可能有一个没有错误的程序3.4.3.2.在你着手开发软件之前,接受这个事实将使你的工作更容易3.5.并不依赖图灵机的语言3.5.1.HTML、XML或正则表达式3.5.2.它们能做的事远远比那些图灵完全的语言少4.不要修复bug4.1.开发团队必须有一个筛选过程,来决定在任何大型项目中要修复哪些bug4.1.1.确定bug的优先级4.2.评估优先级的一个更简单的方法是先使用一个不相关的第二维度,即严重性4.3.优先级是指某个bug对业务的影响4.3.1.如果你的主页上的企业logo丢失了,这或许一点都不严重,然而这件事有最高的优先级4.4.严重性是指它对客户的影响4.4.1.如果你的平台上的一个网页不能工作,这是一个高严重性的问题,因为客户不能使用它4.4.2.但它的优先级可能完全不同,这取决于它是在首页还是只有少数客户访问的不起眼的页面4.5.你区分的级别越多,区分它们就越困难4.6.你应该有关于优先级和严重性的阈值4.6.1.任何低于这个值的bug都会被归类为不修复4.7.寻找bug也会产生成本4.7.1.当务之急是避免关注那些永远不可能被修复的bug4.8.试着在遇到bug的时候就赶紧做是否要修复的决定,这既能为你节省时间,而且还能确保你保持合格的产品质量