元数据

Effective C#:改善C#代码的50个有效方法(原书第3版)

  •  Effective C#:改善C#代码的50个有效方法(原书第3版)|200
  • 书名: Effective C#:改善C#代码的50个有效方法(原书第3版)
  • 作者: 比尔·瓦格纳
  • 简介: 在本书中,世界知名的.NET专家Bill Wagner先生给出了50条建议,告诉你怎样充分利用C# 6.0所具备的特性来编写健壮而高效的代码。这些建议反映了C#开发界的新进展,也令人体会到C#这门语言已经越来越成熟了。除了提出了许多种改善代码品质的新方式,例如怎样发挥泛型的优势之外,本书还针对LINQ给出了很多条建议。
  • 出版时间 2018-04-01 00:00:00
  • ISBN: 9787111597193
  • 分类: 计算机-编程设计
  • 出版社: 机械工业出版社
  • PC地址:https://weread.qq.com/web/reader/2f632f00718f638b2f6cb74

高亮划线

第1条:优先使用隐式类型的局部变量

  • 📌 局部变量的类型推断机制可能会给开发者维护代码造成困难

    • ⏱ 2023-10-26 18:32:28
  • 📌 IQueryable能够把与数据查询有关的多个表达式树组合成一项操作,以便一次执行完毕,而且通常是在存放数据的远程服务器上面执行的

    • ⏱ 2023-10-26 18:34:17
  • 📌 总之,除非开发者必须看到变量的声明类型之后才能正确理解代码的含义,否则,就可以考虑用var来声明局部变量(此处所说的开发者也包括你自己在内,因为你将来也有可能要查看早前写过的代码)。

    • ⏱ 2023-10-26 18:37:09

第2条:考虑用readonly代替const

  • 📌 考虑用readonly代替const

    • ⏱ 2023-10-26 17:42:22
  • 📌 C#有两种常量,一种是编译期(compile-time)的常量,另一种是运行期(runtime)的常量

    • ⏱ 2023-10-26 18:38:25
  • 📌 运行期的常量用readonly关键字来声明,编译期的常量用const关键字来声明

    • ⏱ 2023-10-26 18:38:35
  • 📌 编译期的常量还可以在方法里面声明,而readonly常量则不行。

    • ⏱ 2023-10-26 18:39:28
  • 📌 编译期的常量只能用来表示内置的整数、浮点数、枚举或字符串

    • ⏱ 2023-10-26 18:40:21
  • 📌 readonly常量在执行完构造函数(constructor)之后,就不能再修改了,但和编译器常量不同,它的值是在程序运行的时候才得以初始化的。

    • ⏱ 2023-10-26 18:41:13
  • 📌 eadonly可以用来声明实例级别的常量,以便给同一个类的每个实例设定不同的常量值,而编译期的常量则是静态常量

    • ⏱ 2023-10-26 18:42:18
  • 📌 修改访问级别为public的const常量相当于修改接口,因此,凡是使用该常量的代码都必须重新编译,而修改访问级别为public的readonly常量则相当于修改实现细节,这并不影响现有的客户端。

    • ⏱ 2023-10-26 18:48:47
  • 📌 const常量还有一个地方要比readonly常量好,那就是性能

    • ⏱ 2023-10-26 18:49:35
  • 📌 const关键字用来声明那些必须在编译期得以确定的值,例如attribute的参数、switch case语句的标签、enum的定义等,偶尔还用来声明那些不会随着版本而变化的值。除此之外的值则应该考虑声明成更加灵活的readonly常量。

    • ⏱ 2023-10-26 18:50:22

第3条:优先考虑is或as运算符,尽量少用强制类型转换

  • 📌 优先考虑is或as运算符,尽量少用强制类型转换

    • ⏱ 2023-10-26 18:51:32
  • 📌 有两种办法能够实现转换,一是使用as运算符,二是通过强制类型转换(cast)来绕过编译器的类型检查。

    • ⏱ 2023-10-26 19:05:56
  • 📌 as及is运算符不会考虑由用户所定义的转换,只有当运行期的类型与要转换到的类型相符时,该操作才能顺利地执行。

    • ⏱ 2023-10-27 10:50:53
  • 📌 最大区别在于如何对待由用户所定义的转换逻辑

    • ⏱ 2023-10-27 11:11:33
  • 📌 如果待转换的对象既不属于目标类型,也不属于由目标类型所派生出来的类型,那么as操作就会失败

    • ⏱ 2023-10-27 11:13:19
  • 📌 编译器所考虑的是对象o的编译期类型与目标类型MyType之间有没有转换逻辑,而不是该对象的运行期类型与MyType之间的关系

    • ⏱ 2023-10-27 12:00:10
  • 📌 foreach语句需要同时应对值类型与引用类型,而这种采用cast的类型转换方式使得它在处理这两种类型时,可以展示出相同的行为。但是要注意,由于是通过cast方式来转换类型的,因此可能抛出InvalidCastException异常。

    • ⏱ 2023-10-27 11:27:45
  • 📌 如果想判断对象是不是某个具体的类型而不是看它能否从当前类型转换成目标类型,那么可以使用is运算符。该运算符遵循多态规则

    • ⏱ 2023-10-27 11:30:36
  • 📌 .NET Base Class Library(BCL,基类库)里面有个方法能够把序列中的各元素分别转换成同一种类型,这个方法就是Enumerable.Cast(),它必须在支持IEnumerable接口的序列上面调用 ^26174347-9-7419-7537

    • ⏱ 2023-10-27 11:38:02
  • 📌 涉及泛型的cast操作是不会使用转换运算符的

    • ⏱ 2023-10-27 11:48:44

第4条:用内插字符串取代string.Format()

  • 📌 自己去把它转换成字符串,这样就不用给表达式中的数值装箱

    • ⏱ 2023-10-27 23:44:22
  • 📌 迫使编译器将其理解为条件表达式即可。将整个内容括起来之后,编译器就不会再把冒号视为格式字符串的前一个字符了

    • ⏱ 2023-10-27 23:45:04
  • 📌 过null合并运算符(null-coalescing operator)与null条件运算符(null-conditional operator,也称为null propagation operator(null传播运算符))来更为清晰地处理那些可能缺失的值

    • ⏱ 2023-10-27 23:45:42
  • 📌 在内插字符串里面,还可以使用LINQ查询操作来创建内容,而且这种查询操作本身也可以利用内插字符串来调整查询结果所具备的格式

    • ⏱ 2023-10-27 23:46:37

第5条:用FormattableString取代专门为特定区域而写的字符串

  • 📌 开发者可以利用编译器的类型判定机制来直接生成string或Formattable-String,也可以编写方法,把内插字符串的解读结果转换成适用于某个地区的字符串

    • ⏱ 2023-10-28 00:04:23
  • 📌 不要给这些方法编写以string为参数的重载版本,否则,编译器在面对既可以选string版本又可以选FormattableString版本的情况下,会创建出生成string的程序码,进而调用以string为参数的那个版本

    • ⏱ 2023-10-28 00:05:04
  • 📌 如果需要针对特定的地区及语言来生成字符串,那么就必须根据内插字符串的解读结果来创建FormattableString,并将其转换成适用于该地区及该语言的字符串

    • ⏱ 2023-10-28 00:06:01

第6条:不要用表示符号名称的硬字符串来调用API

  • 📌 C#语言的设计团队意识到了这个问题,并在6.0版本里面添加了nameof()表达式。这个关键字可以根据变量来获取包含其名称的字符串,使得开发者不用把变量名直接写成字面量

    • ⏱ 2023-10-28 00:07:22
  • 📌 某些异常类型的构造函数可以接受string参数,使得开发者能够把该异常所涉及的变量名传给这个参数,从而构造更为明确的异常信息。调用这样的构造函数时,不应该把变量的名字写成硬字符串,而应该使用nameof来获取其名称,以便使代码在变量名改变之后,依然能够正常运作

    • ⏱ 2023-10-28 00:09:19
  • 📌 使用nameof运算符的好处是,如果符号改名了,那么用nameof来获取符号名称的地方也会获取到修改之后的新名字。

    • ⏱ 2023-10-28 00:09:54

第7条:用委托表示回调

  • 📌 委托是一种对象,其中含有指向方法的引用,这个方法既可以是静态方法,又可以是实例方

    • ⏱ 2023-10-28 00:10:55
  • 📌 C#语言提供了一种简便的写法,可以直接用lambda表达式来表示委托

    • ⏱ 2023-10-28 00:11:31
  • 📌 此外,.NET Framework库也用Predicate、Action<>及Func<>定义了很多常见的委托形式 ^26174347-13-1181-1259

    • ⏱ 2023-10-28 00:11:24
  • 📌 predicate(谓词)是用来判断某条件是否成立的布尔(Boolean)函数

    • ⏱ 2023-10-28 00:11:45
  • 📌 Func<>则会根据一系列的参数求出某个结果

    • ⏱ 2023-10-28 00:11:52
  • 📌 编译器不允许在它们之间相互转换

    • ⏱ 2023-10-28 00:12:18
  • 📌 Action<>接受任意数量的参数,其返回值的类型是void

    • ⏱ 2023-10-28 00:12:09

第8条:用null条件运算符调用事件处理程序

  • 📌 做浅拷贝(shallow copy),也就是创建新的引用,并令其指向原来的事件处理程序

    • ⏱ 2023-10-28 19:47:45
  • 📌 当另外一条线程把事件处理程序注销掉的时候,它只会修改类实例中的Updated字段,而不会把该处理程序同时从局部变量handler里面移走,因此,handler中还是保存着早前执行浅拷贝时所记录的那些事件订阅者。

    • ⏱ 2023-10-28 19:48:09
  • 📌 从语义上来看,这与早前的if结构类似,但区别在于?.运算符左侧的内容只会计算一次。

    • ⏱ 2023-10-28 19:48:49

第9条:尽量避免装箱与取消装箱这两种操作

  • 📌 值类型是盛放数据的容器,它们不应该设计成多态类型,但另一方面,.NET Framework又必须设计System.Object这样一种引用类型,并将其放在整个对象体系的根部,使得所有类型都成为由Object所派生出的多态类型。

    • ⏱ 2023-10-28 19:50:33
  • 📌 装箱的过程是把值类型放在非类型化的引用对象中,使得那些需要使用引用类型的地方也能够使用值类型。取消装箱则是把已经装箱的那个值拷贝一份出来。

    • ⏱ 2023-10-28 19:51:01

读书笔记

第1条:优先使用隐式类型的局部变量

划线评论

  • 📌 优先使用隐式类型的局部变量 ^262631416-7MfLjwrk0
    • 💭 多用var,不是手动指定类型
    • ⏱ 2023-10-26 17:46:30

划线评论

  • 📌 这意味着不能盲目地使用var来声明一切局部变量,例如对int、float、double等数值型的变量,就应该明确指出其类型 ^262631416-7MfOGNr9D
    • 💭 防止精度和类型转换出现问题
    • ⏱ 2023-10-26 18:38:02

第2条:考虑用readonly代替const

划线评论

  • 📌 readonly常量是在程序运行的时候才加以解析的,也就是说,如果代码里面用到了这样的常量,那么由这段代码所生成的IL码会通过引用的方式来使用这个readonly量,而不会直接使用常量值本身 ^262631416-7MfPhN4tl
    • 💭 readonly常量是通过引用的方式来使用,const常量则是直接使用数值
    • ⏱ 2023-10-26 18:47:09

第3条:优先考虑is或as运算符,尽量少用强制类型转换

划线评论

  • 📌 静态类型检查机制 ^262631416-7MfPM7NRX
    • 💭 C#的静态类型检查机制是编程语言C#在编译阶段检查类型一致性的过程。这个机制有助于发现和预防在运行时出现的类型错误,提高了代码的可靠性和可维护性。
    • ⏱ 2023-10-26 18:54:37

划线评论

  • 📌 但若用as运算符把装箱的值类型转换成未装箱且可以为null的值类型,则会创建新的对象 ^262631416-7MgRGONrN
    • 💭 as运算符仅可以用于目标类型为引用类型或可空类型的类型转换操作,在转换时一般不会创建新的对象(如果原对象为值类型且目标类型为引用类型,转换成功时则会产生装箱操作,产生新对象);对于目标类型为非可空类型的值类型,可以使用is运算符配合强制转换进行转换
    • ⏱ 2023-10-27 11:10:32

划线评论

  • 📌 如果你想转换的那个对象,其源类型是通过某个泛型参数指定的,那么就要考虑:是给泛型参数施加类型约束(class constraint),还是采用cast运算符来转换类型?如果用后者,那么就需要编写额外的代码来处理不同的情况。 ^262631416-7MgUEbig4
    • 💭 如果目标类型是int这种非空值类型就不能用as
    • ⏱ 2023-10-27 11:55:41

第4条:用内插字符串取代string.Format()

划线评论

  • 📌 在内插字符串里面还可以继续编写内插字符串 ^262631416-7MhFbsTRT
    • 💭 很难读!
    • ⏱ 2023-10-27 23:46:15

本书评论