表2:《框架设计(第2版)》勘误(第2-3次印刷)
页码 | 位置 | Original | Corrected | description |
100 | 第5段 | 根据第4针… | 根据第4章… | 感谢egman |
156 | 中间示例 | public SomeValType(Int32x) { | public SomeValType(Int32 x) { | 感谢egman |
334 | 17.5节第1段 | Equal方法 | Equals方法 | 英文版勘误 |
268 | 第2段代码 | // 此处发生装箱 | // 此处不发生装箱 | 英文版勘误 |
271 | 最后一段 | 当它试图将other转型为SomeValueType时 | 当它试图将o转型为SomeValueType时 | 英文版勘误 |
73页 | 图3.6 | “TypeRef条目表明什么”框右侧出口和下方出口的说明文字 | 右侧出口说明:AssemblyRef:Type在另一个程序集的另一个文件内
下方出口说明: ModuleDef:Type在同一个程序集的同一个文件内 | 排版错误,清晰大图点击图3.6观看 |
90-91页 | 图4.2和图4.3 | 图4.2和图4.3反了 | 调换图4.2和图4.3的图 | 排版错误, 感谢大量网友指正 |
91-92页 | 图4.4和图4.5 | 图和原书不符 | 图4.4应该使用图4.5中的图。而图4.5应该使用原图4.5并在其基础上作如下修改:在线程堆栈中“[返回地址]”的下方再添两项,依次为“length(Int 32)”和“tally(Int 32)”,并在这两项的右边注明“M2局部变量”。 | 排版错误。点击查看清晰大图
|
275页 | 项目列表的第3项“一致性的实现” | 通过为基类型提供一个良好的默认实现,然后开始使用能正常工作并通过良好测试一个类型,以后就可以对需要它的地方做出修改。 | 如果为基类型提供了一个良好的默认实现,那么一开始使用的就是一个能正常工作并且进行了良好测试的类型。以后只需根据自己的需要进行修改即可。 |
|
288页 | 第3段 | 但本来应该像下面这样定义Feedback委托 | 但也可以像下面这样定义Feedback委托 | 感谢谢廷晟 |
317页 | 16.8.1节第一段 | 不需要指定以下特殊的引用类型 | 不能指定以下特殊的引用类型 | 感谢谢廷晟 |
318页 | 第3段 | 而可空的类型不能指定这个约束 | 而可空的类型不能满足这个约束 | 感谢谢廷晟 |
344页 | 倒数第3行 | 但并不是一个理想的结果,因为方法现在是作为一个非空的值传递的, | 但并不是一个理想的结果,因为现在向方法传递的是一个非空的值 | 感谢谢廷晟 |
7页 | 图1.2 | 源文件 | 资源文件 | 图中共4处 |
21页 | 第二个项目列表的第3个项目 | 当然,IL程序集语言不在此列。 | 当然,IL汇编语言不在此列。 |
|
39 | 倒数第4行 | 造成多个单独的程序集 | 更不能分散到多个单独的程序集中 |
|
41 | 倒数第4段,第2行 | 所以会在JeffTypes.dll中添加一个包含清单元数据表的DLL PE文件。 | 所以JeffTypes.dll是一个包含清单元数据表的DLL PE文件。 |
|
47 | 表2.4的表题 | …及其对应的AL.exe形状… | …及其对应的AL.exe开关… |
|
51 | 倒数第2段 | 假定最终要求的最终目录结构是: | 假定最终要求的目录结构是: | 多了个最终 |
66 | 3.4节第1段 | 任何时候生成一个程序集,程序集会包含... | 任何时候生成一个程序集,程序集都会包含... | better |
72 | “重要提示”的第1段 | 对于.NET Framework配套提供的程序集来说,如果引用的是其中定义的方法和类型,那么刚才的讨论是正确的。 | 如果引用的不是.NET Framework配套提供的程序集所定义的方法和类型,那么刚才的讨论是正确的。 | 意思反了 |
117 | 代码的倒数第3条注释 | p2不需要装箱,因为调用的是CompareTo(object) | p2需要装箱,因为调用的是CompareTo(object) | 感谢Eri |
75-76 |
| 除此之外,这里展示的元素只能在一个发布者策略配置文件中指定; | 除此之外,这里展示的元素是发布者策略配置文件唯一能指定的元素; |
|
76 | 项目列表的第2条,第3行 | 将JeffTypes的版本1.0.0.0重定向到版本。 | 将JeffTypes的版本1.0.0.0重定向到版本2.0.0.0。 | 最后遗漏了2.0.0.0。 |
82 | 表4.2第一行 | 并将新对象实例的各个字段设置成与this对象实例的字段完全一致 | 并将新对象实例的各个实例字段设置成与this对象实例的实例字段完全一致 | 字段前遗漏了“实例”两字。作者在这段话里引用了两种不同的“实例”。一种是类的实例,也就是对象;另一种是类中定义的实例字段。 |
83 | 4.2节第1段,第2行 | 由于这个方法是非虚方法的, | 由于这个方法是非虚方法, | 多了个“的”字 |
90 | 第2段,第1行 | 图4-2展示了CLR加载的一个Microsoft Windows进程。 | 图4-2展示了已加载了CLR的一个Microsoft Windows进程。 | 意思反了 |
97 | 图4.13中的标注 | Type对象 | Type类型对象 |
|
97页 | 最后一段,第3行 | 我们总算理解了CLR的整个类型对象 | 我们总算理解了CLR的整个类型系统 |
|
100 | 倒数第3行 | 因为大的Int32位数字 | 因为大的Int32数字 | 多了一个“位” |
107 | 补充内容的第1段 | 同时正确排列和打包数据字段 | 同时正确排列和填充数据字段 |
|
110 | 第2段第4行 | 拆箱不涉及任何字节在内存中的副本 | 拆箱不涉及任何字节在内存中的复制 | 编辑错误 |
111 | 第2段第1行 | 上述代码底部的一行惟一的目的 | 上述代码底部的三行惟一的目的 |
|
112 | 第1段第3行 | 再将v的已装箱复制的地址 | 再将v的已装箱拷贝的地址 | 编辑错误 |
114 | 倒数第1段 | 来接受值类型 | 来接受你的值类型 | 编辑错误 |
120 | "重要提示"第2段第1行 | 许多开发人员审阅了本章的章节 | 许多开发人员审阅了本书的章节 |
|
120 | 5.5节的标题 | 对象相等性和身份标识 | 对象相等性和同一性 | 仅标题错 |
121 | 倒数第2个代码块 | 如果对象属于不同的对象,则肯定不相等 | 如果对象属于不同的类型,则肯定不相等 |
|
122 | 项目列表之二 | 覆盖==和!=操作符方法 | 重载==和!=操作符方法 |
|
123 | 第4段,第4-7行 | 键对 | 键对象 | 编辑错误,总共5处 |
188 | 10.1.2节第3行 | NewMail类中事件成员 | MailManager类中事件成员 |
|
189页 | 倒数第4行代码 | 如果类是封装的, | 如果类是密封的, | 后文的non-virtual应翻译出来,即“非虚” |
192 | 倒数1段第3行 | 并将Fax对象的引用作为参数传递给MailManager对象 | 构造时传递MailManager对象引用作为参数。 |
|
193 | 第4段第1-2行 | FaxMsg方法的引用传递给MailManager对象作为它的第一个参数, | FaxMsg方法传递MailManager对象引用作为它的第一个参数, |
|
90 | 第1段第2行 | 将解释发出调用的静态方法...之间的区别 | 将解释调用静态方法...的区别 |
|
308 | 倒数第2段,第2行 | 来指出哪些类型的实参是有效的 | 来指出哪些类型实参是有效的 |
|
308 | 倒数第1段,第2行 | 实际是在CLR中定义一个新类型,新类型的对象... | 实际是在CLR中定义一个新类型对象,新类型对象... |
|
314 | 第一个标题 | 泛型方法和类型接口 | 泛型方法和类型推导 | 仅标题错 |
316 | 倒数第1段,第3行 | CLR允许基于类型参数名称或者约束来进行重载 | CLR不允许基于类型参数名称或者约束来进行重载 |
|
262 | 14.2节标题上方第一行 | 都可以替换成实现接口的一个类型的实现。 | 都可以替换成实现接口的一个类型的实例。 |
|
263 | 第1行 | CLR会为接口类型的对象 | CLR会为接口类型对象 |
|
266 | 第2段第4行 | 或String实现的其他任何接口。 | 或String实现的其他任何接口的方法。 |
|
272 | 第2段,第5行 | 来满足IComparable.contrct | 来满足IComparable的contract | 排版错误 |
274 | 第2段,第2行 | 充当了Derived的IComparable。接口的...具体做法是声明没的IComparable接口Derived类 | 充当了Derived的IComparable接口的...具体做法是声明没有IComparable接口的Derived类 | 排版错误 |
274 | 第3段第3行 | 希望继承类 | 希望派生类 |
|
275 | 倒数第3段,倒数第2行 | 已经从Stream类继承了执行同步I/O操作。 | 已经从Stream类继承了执行异步I/O操作的能力。 |
|
206 | 第2段,第3行 | ToUpperInvariant、Substring和创建的两个临时字符串......便在堆上创建 | ToUpperInvariant和Substring创建的两个临时字符串......会在堆上创建 | 排版错误 |
207 | 第1行 | 原因是假如大小写不同 | 原因是假如只是大小写不同 |
|
209 | 倒数第1段,第1行 | CompareInfo类型的Compare方法从CompareOptions枚举类型获取一个参数值。 | CompareInfo类型的Compare方法获取来自CompareOptions枚举类型 的一个值作为参数。 | 避免误解 |
225 | 11.4.3节第3行 | 需要格式化成一个对象的时候 | 需要格式化成一个字符串的时候 |
|
221 | 第2段第3行 | 它实现了 | 它是实现了 |
|
241 | 中间一个代码段的注释 | (泛型格式) | (常规格式) | 替换错误 |
241 | 倒数第1个“注意” | 泛型格式 | 常规格式 | 替换错误 |
242 | 第2个代码段的注释 | 泛型格式 | 常规格式 | 替换错误 |
244 | 第1行 | 否则可能引起名称冲突 | 除非担心引起名称冲突 |
|
250 | 倒数第1段代码,注释 | CLR检查String数组中oa的元素类型 | CLR检查oa的元素类型是不是String;正常 |
|
250 | 倒数第1段代码,注释 | CLR检查Int32中oa的元素类型 | CLR检查oa的元素类型是不是Int32 |
|
251 | 第3行 | 在本例中,它没有进行检查 | 在本例中,它不包含Int32元素 |
|
251 | 13.2节2段第3行 | 都将继承自FileStream[]类型 | 都将由FileStream[]类型继承 |
|
281 | 第4段 | 因为对象内部的代码可以访问对象的实例方法成员 | 因为对象内部的代码可以访问对象的实例成员 |
|
291 | 第2段 | 其中的button1.Click | 其中的button1_Click |
|
326 | 倒数第2行 | 下面这行代码具有相同的功用 | 下面这几行代码具有相同的功用 |
|
332 | 表17.1倒数第3行 | 多具实例 | 多个实例 |
|
337页 | 第2段,第5行 | 通常,我们可以使用CustomAttributeData类,分析通过Assembly的静态方法ReflectionOnlyLoad(也在第22章讨论)在一个程序集的元数据中加载的属性。 | 通常,是先用Assembly的静态方法ReflectionOnlyLoad(也在第22章讨论)加载一个程序集,再用CustomAttributeData类分析这个程序集的元数据中的attribute。 |
|
337页 | 第3段, 第1-2行 | 也就是说,调用它时,它会返回 | 也就是说,调用它时,它会在 |
|
表1:《框架设计(第2版)》勘误(第1次印刷)
页码 | 位置 | original | corrected | description |
XVII | 倒数第1段 | 虽然本书用C#来演示… | 虽然本书用C#语言来演示… | 英文版勘误 |
45 | 第三段 | 由于使用了/main:App.Main命令行开关 | 由于使用了/main:Program.Main命令行开关 | 英文版勘误 |
82 | 表4.1对Tostring的说明的最后一句话 | 第12章将更详细地讨论ToString。 | 第11章将更详细地讨论ToString。 | 英文版勘误 |
121 | 编号列表之4 | 如果基类的Equals方法返回,就返回false;否则返回true。 | 如果基类的Equals方法返回false,就返回false;否则返回true。 | 感谢Jeriffe 少了一个false |
122 | 最后一段 | …以便在CompareTo方法返回0的时候返回true。 | …以便在CompareTo方法返回0的时候返回true(这对某些类型是成立的,但对另一些类型则不然。您必须考虑类型是如何使用的,以及CompareTo对于该类型的含义)。 | 英文版勘误 |
135 | 第一段 | 以Visual Studio 2005为例~本段尾 | 自Visual Studio 2005起,创建新的Windows窗体、Web窗体、用户控件时,Visual Studio会创建两个源代码文件 :一个用于存储用户的代码,另一个用于存储设计器生成的代码。因为设计器生成的代码位于一个单独的文件,所以偶然编辑它的可能性就会很小。 | 英文版勘误 |
135 | 第二段 | 两个文件的类型中都采用关键字partial。 | 所有文件中的类型都应用了partial关键字。 | 英文版勘误 |
165 | 第一个code sample | To Int32 | ToInt32 | 排版错误 |
166 | 第一个code sample | To Single | ToSingle | 排版错误 |
92~97 | 图4.6~图4.13 | GetProgressReport | GenProgressReport (x3) | 英文版勘误 |
241 | 第二段 | 也可以隐式或显式地将一个枚举类型实例转型为一个数值类型。 | 也可以显式地将一个枚举类型实例转型为一个数值类型。 | 英文版勘误 |
1. [位置]:130页第1行
[原文]:中的一些成员编译器,还为它们...
[更正]:中的一些成员,编译器还为它们...
2. [位置]:131页倒数第3段
[原文]:那么该模块将成为友元程序集的一部分,
[更正]:而且该模块将成为友元程序集的一部分,
3. [位置]:131页10行。
[原文]:从友元程序集中访问上述程序集中的internal类型没有什么价值。
[更正]:从友元程序集中访问上述程序集中的internal类型非常容易。
4. [位置]:132页倒数8行。
[原文]:尽管编程语言的编译器忽略可访问性的检查,但是在运行时检验IL代码确保已引用成员的可访问性是值得的,
[更正]:通过对IL代码进行验证,可确保所引用的一个成员的可访问性在运行时得到正确兑现——即使语言的编译器忽略了对可访问性的检查。
5. [位置]:133页,6.4节上方的那一行
[原文]:那么,CLR将声称该方法不能强制实施
[更正]:那么,CLR的承诺就无法兑现了。
6. [位置]:134页倒数8行。
[原文]:而且有一程序开发人员在没有使用源代码控制方法校验类型定义的情况下对源代码进行了签出。
[更正]:一个程序员把它从源代码控制系统中签出(check out)以进行修改。
7. [位置]:134页到135页,“代码拆分器”这一条
[原文]:[不重复了。原译者将VS的designer理解成“设计中心”,造成大量理解错误]
[更正]:代码拆分 在Microsoft Visual Studio中创建新的Windows窗体或Web窗体项目时,会在项目中自动生成一些源代码文件。这些源代码文件包含一些模板,可通过它们快速上手,按部就班地构建当前类型的项目。使用Visual Studio设计器在Windows窗体或Web窗体上拖放控件时,Visual Studio会自动生成源代码,并将代码拆分到不同的源代码文件中。这确实有助于提高开发效率。在很久以前(VS2005以前),生成的代码是直接放到当前正在处理的那个源代码文件中的。这样做的缺点在于,如果不小心编辑了一下生成的代码,设计器行为就可能变得失常。从Visual Studio 2005开始,新建一个Windows窗体、Web窗体或者用户控件时,Visual Studio会自动创建两个源代码文件:一个用于你的代码,另一个用于设计器生成的代码。由于设计器的代码在一个单独的文件中,所以基本上杜绝了你不小心编辑到它的可能。
8. [位置]:137页,第1段第1行
[原文]:将示范这些关键字的值和使用方法
[更正]:将示范这些关键字的价值和使用方法
9. [位置]:139页3行。
[原文]:当然,问题在于,为什么C#编译器只生成call指令,而不是其他指令呢?答案就是因为C#的工作组决定JIT编译器应生成验证所使用的对象的代码,以确定调用不为null。
[更正]:那么,为什么C#编译器不干脆生成call指令呢?答案就是因为C#工作组决定JIT编译器应生成代码来查证发出调用的对象不是null。
10. [位置]:140页倒数9行。
[原文]:而且组件对于不同的时间进度表其版本也不同。
[更正]:而且这些组件的版本发布一般都基于不同的日程安排。
11. [位置]:141页3行。
[原文]:如果类最初是密封的,将来就无需破坏兼容性将类改变成非密封的。
[更正]:如果类原来是密封的,将来把它改为非密封的并不会破坏兼容性。
12. [位置]:141页5行。
[原文]:另外,如果非密封的类定义了非密封的虚方法,那就必须在新版本的类中保存虚方法的自定义调用,否则将来可能会破坏派生类的兼容性。
[更正]:另外,如果非密封的类定义了任何非密封的虚方法,就必须在新版本的类中保持虚方法的调用顺序,否则可能中断派生类。
13. [位置]:141页9行。
[原文]:但是,如果JIT编译器通过使用密封类型将调用看成虚方法,……
[更正]:但是,如果JIT编译器看到一个使用密封类型对虚方法的调用,……
14. [位置]:141页倒数2行。
[原文]:这将允许基类改变它不影响派生类的观点。
[更正]:这样一来,因为知道不会影响到派生类,所以可以放心地对基类进行修改。
15. [位置]:143页第一行
[原文]:可以定义一些辅助类型来密封离散的函数块
[更正]:我会定义一些辅助类型来封装独立的功能
16. [位置]:150页倒数17行。
[原文]:当某个字段是引用类型,并且该字段标记为readonly时,它就是不可改变的引用,而不是字段所引用的对象。
[更正]:如果一个字段属于引用类型,并且被标记为readonly时,不可改变的是这个引用本身,而不是它所引用的对象。
17. [位置]:152页,“重要提示”上方的一段
[原文]:...以及向新对象复制源对象的类型。
[更正]:...以及向新对象复制源对象的字节。
18. [位置]:153页,第一段代码
[原文]:Int32x和Strings
[更正]:Int32 x和String s
19. [位置]:155页,第3行
[原文]:那么,m_topLeft字段和m_bottomRight字段中的Point字段的...
[更正]:那么两个Point字段中的...
20. [位置]:155页,第3段第1行(第2段代码下)
[原文]:m_topLeft字段和m_bottomRight字段中的Point字段的...
[更正]:两个Point字段中的...
21. [位置]:158页第1行
[原文]:所以最后合在类型构造器中初始化类型的单实例对象。
[更正]:所以最适合在类型构造器中初始化类型需要的任何单实例对象。
22. [位置]:159页倒数第2行
[原文]:JIT编译器需要决定它是否必须生成一个调用来执行方法中的类型构造器。
[更正]:JIT编译器需要决定它是否在方法中生成对类型构造器的调用。
23. [位置]:163页的“注意”,第2 行
[原文]:会发现FCL中没有定义任何操作数重载方法
[更正]:会发现这些类型没有定义任何操作符重载方法
24. [位置]:165页第2段最后一行
[原文]:转换符构造器
[更正]:转换构造器
25. [位置]:165页第3段倒数第2行
[原文]:应认真考虑什么转换符
[更正]:应认真考虑什么转换构造器
26. [位置]:168页,第4行
[原文]:对大量值类型
[更正]:对大的值类型
27. [位置]:169页第1行
[原文]:为什么C#要求在调用方法的时候指定out或ref参数?毕竟,C#编译器知道调用的方法是否需要out或者ref参数,并且能够...
[更正]:为什么C#要求在调用方法的时候指定out或ref?毕竟,C#编译器知道调用的方法需要的是out还是ref,所以能够...
28. [位置]:174页8.8节第1段,第1-2行
[原文]:....并且最好在基类上定义接口....最好使用接口(如IEnumerable<T>)来声明方法..
[更正]:...最好是接口而不是基类... 最好使用接口(如IEnumerable<T>)来声明方法参数..
29. [位置]:174页8.8节第3段,第3行
[原文]:传递数组和其他任何实现了IList<T> 类型的对象
[更正]:传递数组和实现了IList<T>的其他类型的对象。
30. [位置]:175页,8.9节第2段,第3行
[原文]:即通过抛弃const修饰符改变对象或参数,
[更正]:即通过强制转型来去掉“常量性”,
31. [位置]: 175页,8.9节第2段,最后一句话
[原文]:从这种意义上讲,未托管的C++语言依赖编程人员来确保常量对象或参数不被赋值,即便他们可以这样做。
[更正]:从某种意义上说,未托管的C++语言向程序员撒了一个谎,使他们以为常量对象或实参不能写入(事实上可以)。
32. [位置]:177页,第3段,第3行
[原文]:通常称为索引器方法(index method)。可以选择索引器方法...稳健性...
[更正]:通常称为访问器方法(accessor method)。索引器方法可以选择...合理性...
33. [位置]:178页,第2行
[原文]:而组件还需要做额外的工作:
[更正]:所以额外的工作是值得的:
34. [位置]:178页倒数19行。
[原文]:属性不能被重载,也就是说,如果其类型不同,两个属性可以同名。
[更正]:属性不能重载;也就是说,不能定义名称相同、类型不同的两个属性。
35. [位置]:180页,项目列表之二,第1行
[原文]:如果在一行中...
[更正]:如果连续...
36. [位置]:182页第2段第1行
[原文]:创建一个索引器然后在相关的数组中...
[更正]:创建一个索引器然后在关联数组中...
37. [位置]:183页第3段,第3行
[原文]:我们可能希望改变索引器的默认名称,使索引器拥有get和set访问器方法,
[更正]:(全部删除)
38. [位置]:185页,9.4节第2段,第2行
[原文]:set访问器方法声明为protected属性
[更正]:set访问器方法声明为protected
39. [位置]:353页,第3段,第1-2行
[原文]:即最常用的异常类型应首先出现
[更正]:即最具体的异常类型应首先出现
40. [位置]:355页倒数8行。
[原文]:所有未抛出的异常将被封装入一个System.Runtime.CompilerServices.RuntimeWrappedException。
[更正]:引发的所有非异常均被包装在System.Runtime.CompilerServices.RuntimeWrappedException中。
41. [位置]:365页,“注意”的倒数第3行
[原文]:但异常可以这样使用
[更正]:而应使用异常
42. [位置]:369页,第2段,倒数第2行
[原文]:但是,异常又被称为EndXxx方法的线程...
[更正]:但是,异常又被调用EndXxx方法的线程...
43. [位置]:374页2行。
[原文]: TryXxx方法的Boolean类型的值返回false时表示仅有一个类型转换失败。但是方法仍抛出其他类型失败的异常。
[更正]: TryXxx方法的Boolean返回值如果为false,那么代表的只是一种类型的错误。方法仍要为其他类型的错误抛出异常。
44. [位置]:380页第1段第1行。
[原文]:托管堆栈
[更正]:托管堆
45. [位置]:388页,第4段,第一行
[原文]:JIT编译器将检查是否有程序集定义的方法在编译时没有被优化,以及是否有进程在调试器中正在执行。
[更正]:JIT编译器将检查定义方法的程序集在编译时是否优化,以及进程是否在调试器中执行。
46. [位置]:388页,第6段,最后一行
[原文]:编译器开关可以阻止上述优化
[更正]:编译器开关会重新启用优化
47. [位置]:390页,第2段最后一行
[原文]:则在block块中生成。
[原文]:则在finally块中生成。
48. [位置]:390页,20.4.1节上面一段,倒数第2行
[原文]:本地资料....本地资料...
[原文]:本地资源....本地资源...
49. [位置]:391页3行
[原文]:在对象之上编译这些方法可以确保一点:
[更正]:在构造对象时编译这些方法确保了
50. [位置]:391页倒数13行(代码的注释)
[原文]:本地资源就是关闭的。
[更正]:本地资源将被关闭。
51. [位置]:392页,代码第6行
[原文]:不采用
[更正]:激活
52. [位置]:394页,第2段,倒数第4行
[原文]:并传递到CreateEvent函数返回的句柄值中。
[更正]:并在构建时传递从CreateEvent返回的句柄值。
53. [位置]:395页2行
[原文]:就可以不用SafeHandle对象来访问句柄
[更正]:就可以从一个SafeHandle类型的对象中获取句柄
54. [位置]:397页10行
[原文]:大家还需要注意下述事实,即当执行Finalize方法时,我们没有办法控制它。
[更正]:注意无法控制Finalize方法的执行时间。
55. [位置]:397页13行
[原文]:但是,在访问值类型实例或者引用类型对象时不定义Finalize方法将相当好。
[更正]:但是,完全可以访问值类型的实例或未定义Finalize方法的引用类型的对象。
56. [位置]:397页倒数20行
[原文]:Finalize方法在垃圾收集结束时被调用,有5种事件会导致一个对象的Finalize方法被调用:”
[更正]:下列五种事件会启动垃圾收集,收集结束时会调用Finalize方法:
57. [位置]:397页倒数9行
[原文]:减少进程工作组的大小
[更正]:减小进程工作集的大小
58. [位置]:399页倒数13行
[原文]:在终结可达队列中出现的指针标识该对象的Finalize方法即将被调用。
[更正]:终结可达队列中的每个指针都代表其Finalize方法已准备好调用的一个对象。
59. [位置]:399页倒数3行
[原文]:我们不应该执行对正在执行代码的线程做任何假设的Finalize方法中的任何代码。
[更正]:Finalize中的代码不应该对执行代码的线程做出任何假设。
60. [位置]:400页第1段,第3行,括号中的内容
[原文]:(那个没有分配可终结对象的线程)
[更正]:(它可能跟不上分配的速度)
61. [位置]:403的“重要提示”
[原文]:如果类定义了一个字段的类型实现了释放模式的字段
[更正]:如果类定义了一个字段,而且该字段的类型实现了Dispose模式
62. [位置]:403页倒数第1行,404页第1行
[原文]:类是封装的...类不是封装的
[更正]:类是密封的...类不是密封的
63. [位置]:405页中间那段代码
[原文]: (缺少代码)
[更正]:在“// 删除临时文件”上方插入以下两行代码:
// 写入结束后显式关闭文件
fs.Dispose();
64. [位置]:408页倒数16行。
[原文]:Mutex类确实实现了IDisposable接口,但是在释放本地资源时调用了Dispose方法,并且Mutex类不对锁本身做任何处理。
[更正]:Mutex类确实实现了IDisposable接口,但是在它上面调用Dispose只会释放本地资源;不会对锁本身进行任何处理。
65. [位置]:409页,20.11节的第1段,倒数第2行;代码注释中的倒数第2行;以及409页倒数第2行
[原文]:BinaryWriter
[更正]:StreamWriter
66. [位置]:410页,第2个“注意”
[原文]:“.NET Framework 2.0提供了一个称为托管调试助手(MDA)的辅助调试应用程序。启用MDA后,.NET Framework查找公共的编程错误,并且启动一个相应的MDA。在调试器中,MDA就像抛出一个异常。检测到StreamWriter对象在被垃圾收集之前没有显式地进行关闭时,就会出现一个可以利用的MDA。
[更正]:.NET Framework提供了一个名为托管调试助手(Managed Debugging Assistant,MDA)的功能。启用一个MDA后,.NET Framework会查找特定种类的常见的编程错误,并激活对应的MDA。在调试器中,激活一个MDA感觉就像抛出一个异常。有一个MDA可以检测StreamWriter对象没有显式关闭就作为垃圾被回收的情况。
67. [位置]:411页代码中的注释
[原文]:索引为0,则返回true的实例属性
[更正]:索引不为0,则返回true的实例属性
68. [位置]:412页16行。
[原文]:我们可以以GCHandle实例作为参数调用Free方法(该方法通过将IntPtr字段设为0也会使实例无效)。
[更正]:可以获取GCHandle实例,并在这个实例上调用Free方法。Free方法会将IntPtr字段设为0,使实例变得无效。
69. [位置]:414页4行
[原文]:MailManager对象将返回的GCHandle实例保存在一个垃圾收集中(而不是保存委托对象的引用)。
[更正]:MailManager对象在一个集合中保存所返回的GCHandle实例,而不是保存对该委托对象的引用。
70. [位置]:414页倒数第2段,第1行
[原文]:WeakReference类底部的对象也称为类。
[更正]:WeakReference类的缺点在于它是一个类。
71. [位置]:416页倒数6行
[原文]:当内存充满或者接近充满时,垃圾收集不会发生。相反,无论何时,只要第0代充满,垃圾收集操作就会发生,”
[更正]:垃圾收集不是发生在内存满或即将满时,而是每当第0代满时就发生。
72. [位置]:417页,20.13节第3段
[原文]:如果Finalize方法执行的代码中放了一个指向静态字段中的对象的指针
[更正]:如果Finalize方法执行的代码将对象指针放到一个静态字段中
73. [位置]:418页倒数17行
[原文]:更常见的做法是在Finalize方法内部设置一个根来引用对象从而使对象复苏。
[更正]:更常见的做法是在Finalize方法中条件性地设置一个根来引用对象。
74. [位置]:422页倒数15行。
[原文]:实际上,大多数应用程序的根生存在参数或者局部变量的线程的堆栈中。
[更正]:实际上,应用程序的大多数根都以参数或局部变量的形式存在于线程的堆栈上。
75. [位置]:423页第1段第3行
[原文]:4位或者8位
[更正]:4字节或8字节
76. [位置]:426页第1段
[原文]:我们不得不做大量的工作来进行恢复,现在,不必这样做了。我们只需抛出异常,程序会顺利地进行恢复。
[更正]:在这种情况下,以前做的大量工作都算是白做了。另外,需要捕捉这个异常,使程序得体地恢复。
77. [位置]:428页,倒数第1段代码上方,倒数第1行
[原文]: 确定一个对象所处的代码
[更正]: 确定一个对象所处的代
78. [位置]:432页,唯一一段代码的上方
[原文]:告诉CLR使用并发垃圾收集
[更正]:告诉CLR不使用并发垃圾收集
79. [位置]:439页倒数第2段,括号中的内容
[原文]:尽管在一个单独的Windows进程中可以运行多个应用程序域,而且还没有硬编码限制
[更正]:尽管在一个Windows进程中可以运行的AppDomain数量没有硬性限制
80. [位置]:440页倒数6行。
[原文]:然而,共享这些资源的收益没有与投入成正比。
[更正]:可惜共享这些资源所带来的好处是有代价的。
81. [位置]:446页倒数8行。
[原文]:在任意时刻,一个应用程序域只能执行一个线程。
[更正]:在任意时刻,一个线程只能在一个应用程序域中。
82. [位置]:447页倒数4行。
[原文]:因为CLR实际上要结束调用方法来执行字段的访问。
[更正]:因为CLR最终要通过调用方法来执行字段访问。
83. [位置]:457页第3行。
[原文]:正在执行托管代码块中的操作
[更正]:正在执行托管的阻塞(blocking)操作
84. [位置]:461页中间一段 ,第3行
[原文]:然后将提取到的标识信息返回到System.Reflection.AssemblyName对象中
[更正]:然后以一个System.Reflection.AssemblyName对象的形式返回这些信息
85. [位置]:465页,第3段,第3行
[原文]:应用程序域第一次访问这个类型时,CLR构造一个该类型的实例,并初始化对象的字段....
[更正]:一个类型在一个应用程序域中被首次访问时,CLR会构造RuntimeType的一个实例,并初始化这个RuntimeType对象的字段...
86. [位置]:466页1行
[原文]:“我们可以为GetType方法传递一个合乎程序集类型条件(assembly-qualified type)的字符串,例如……。”
[更正]:可向GetType传递一个限定了程序集的类型字符串,比如...
87. [位置]:466页第一个“注意”
[原文]:Microsoft为类型名称和合乎程序集类型条件...
[更正]:Microsoft为类型名称和限定了程序集的类型名称...
88. [位置]:466页,倒数第2行
[原文]:返回类型定义的程序集或者模块的名称和类型全名。
[更正]:返回定义该类型的那个程序集或模块的名称,或者返回类型的全名。
89. [位置]:469页13行
[原文]:ObjectHandle类型允许我们在一个应用程序域中创建一个对象,然后将其传递到其他的应用程序域中,但不能强制对象进行具体化(materialize)。准备具体化该对象时,可以调用ObjectHandle的Unwrap方法。该方法加载在调用Unwrap方法的应用程序域中定义的被具体化的类型的程序集。
[更正]:ObjectHandle类型允许将一个AppDomain中创建的对象传至其他AppDomain,期间不强迫对象具体化(materialize)。要具体化该对象时,请调用ObjectHandle的Unwrap方法。在一个AppDomain中调用该方法时,它会将定义了要具体化的类型的程序集加载到这个AppDomain中。
90. [位置]:478页,第2段最后一行
[原文]:只有设置BindingFlags的IgnoreCase,这些方法才有用武之地。
[更正]:在这个时候,BindingFlags的IgnoreCase标志就有用了。
91. [位置]:487页,倒数第1段,第2行
[原文]:保存在一个相同类型的集合中
[更正]:保存在某种形式的一个集合中
92. [位置]:497页4行
[原文]:为了让线程继续运行,线程可以在线程池的队列中加入一个工作项,让线程池中的线程来执行打开文件的代码。当然,这必须在拥有合适许可权限的程序集中进行。这种‘工作区’智取安全权限的现象可以允许怀恶意的代码对受限资源进行严重破坏。为了阻止这种获得安全权限的方式,……
[更正]:为了绕过这种限制,该线程可能会把一个工作项加入线程池的队列,让线程池中的线程执行打开文件的代码。当然,这得在拥有合适权限的程序集中进行。这种“变通”规避了安全机制,让恶意代码得以对受限资源造成重大破坏。为了阻止这种瞒天过海的花招,……
93. [位置]:513页倒数16行。
[原文]:用者在执行异步I/O操作时就可以使用这个IasyncResult对象了。
[更正]:调用者现在就可以按类似于执行异步I/O操作时所用的套路来使用这个对象了。
94. [位置]:526页中间部分的代码。
[描述]:翻译错误加印刷错误。
[评论]:1. 所有注释中的“自动执行”,其英文为“Atomically performs”,应该译为“原子性地执行”。2. Decrement方法的注释应该是“//原子性地执行(location--)”。3. Add方法的注释第一行应该是“//原子性地执行(location+=value)”;第二行原译“//说明:值可以是一个可进行减法算的负值”不正确,应该译为“//说明:value可以是负值,以实现减法运算”。
95. [位置]:528页15行。
[原文]:“所有的开发人员不得不在每个需要线程同步的方法中编写必须的代码以进入或者离开该字段。”
[更正]:开发人员要做的只是在每一个需要线程同步的方法中按要求编写代码以进入和离开该字段。
96. [位置]:535页5行。
[原文]:“Monitor类应该被设计为这样,即我们可以利用它构建一个Monitor类型的静态实例从而创建一个线程同步锁。而且静态方法应该是可以操作锁对象本身的实例方法,因此,我们就不必对Monitor的任何方法传递System.Object参数。这样会解决所有的问题,并为所有的开发人员简化编程模型。顺便说一下,前面的代码在同步静态方法上表现一般,如果要同步静态方法,只需将所有成员的修饰符改为static,之后代码就可以正常工作了。”
[更正]:它的设计应该允许你构建一个Monitor类型的实例,以创建一个线程同步锁。这样的话,那些静态方法就应该改为对锁对象自身进行操作的实例方法,于是,你也不用再给Monitor类的任何方法传递一个System.Object参数了。这将会解决所有的问题,并为所有开发者简化编程模型。顺便提一句,前述代码稍做修改便可用于同步静态方法:只须把所有成员改为静态即可。
97. [位置]:535页倒数7行。
[原文]:该技术不著名是因为它不是那么有趣或者有用。说它著名是因为有许多资料描述它。
[更正]:这一技术之所以有名,并非因其特别有趣或有用,而是因为有很多关于它的文献资料。
98. [位置]:535页倒数2行。
[原文]:“最后结果是,Microsoft果断地在内存模型上达成一致,也即CLR将使用该技巧并对其明确存档。…… 因为CLR在采用的内存模型上达成了一致,所以双检锁技巧在CLR中就根本不再是一个问题了。”
[更正]:Microsoft最终设计出那个CLR后来采用的内存模型,并且为其提供了清楚的文档说明。…… 因为CLR采用的这个内存模型的缘故,双检锁技术在CLR平台上根本不成其为一个问题。
99. [位置]:536页代码中s_lock字段上方的注释。
[原文]:“s_lock对象用来实现线程安全,在定义这个对象时我们假设创建单个对象比创建一个System.Object对象的开销要昂贵,并且还假设创建单个对象完全没有必要。否则,在类的构造器中创建单个对象就比较高效和简单了。”
[更正]:s_lock用于实现线程安全。提供这个对象,是基于如下假设:首先,创建该单体对象的开销高于创建一个System.Object对象;第二,该单体对象也许根本没必要创建。若非如此,在类构造器中创建该单体对象会更有效和方便。
100. [位置]:537页24行。
[更正]:此外,创建单体对象的开销必须是远高于创建用于加锁的对象,否则应用这种技术就没有多少收益可言。
101. [位置]:538页12行。
[原文]:“因此在使用ReaderWriterLock时,如果希望让写线程等待处理,就可以给予写线程更高的优先级(相较于读线程)。我们知道有一些人们准备使用ReaderWriterLock类,但是由于这个类采用了读线程比写线程较高优先级的策略,所以写线程有时会发生饥饿,不能及时完成任务。”
[更正]:所以在使用ReaderWriterLock的时候,如果有写线程处于等待状态,你会希望它们能得到高于读线程的优先级。我见过一些人使用ReaderWriterLock,由于该类厚读薄写的策略,写线程总处于时间饥渴状态并因此难以及时完成任务。
102. [位置]:538页17行。
[原文]:“它使得下述情况非常普遍:……”
[更正]:“这种情况正变得更普遍:……”