书评:“MCSD 认证工具包(考试 70-483):C# 编程”






4.93/5 (18投票s)
“MCSD 认证工具包(
引言
我最近通过了Microsoft 70-483 "C# 编程" 认证,这是进入 .NET 和 WinRT 开发者认证周期的入门点之一。
为了充分准备,我完整阅读了两本专门针对此认证的书籍:《MCSD 认证工具包 (考试 70-483):C# 编程》 和 《考试参考 70-483:C# 编程》
。
如果你打算通过认证,我强烈建议你阅读这两本书。
事实上,这两本书是我准备认证的主要材料,它们完美地完成了任务。
本文是对《MCSD 认证工具包 (考试 70-483):C# 编程》 的完整评论。
这是你首先应该阅读的书,因为我认为它是最能帮助你获得认证的书,但从技术角度来看,它却自相矛盾地最糟糕,你很快就会明白为什么。
不幸的是,这是这本书最大的缺陷:它包含大量技术错误,从只有专家才能发现的、对新手无害的基本词语误用,到不良实践,包括错误断言,这更令人恼火。有时,在一个章节中,你既能找到一些非常好的内容,也能找到一些非常糟糕的内容。
本文不是一份完整的勘误表,因为我没有记录下每一个错误,但我会尽量说明主要问题,以便在不穷举的情况下建立一个分类。
我选择先介绍最糟糕的内容——不良实践,然后是最好的内容——良好实践,因为这本书同样充满了大量非常好的技术内容,非常适合认证和你在日常开发工作中使用。
糟糕的部分
一些不良实践
教授不良实践,尤其是在你的受众大部分由非经验丰富的开发者组成时,可能是一本技术书籍能做的最糟糕的事情。
然而,说实话,幸运的是这本书中只有少数不良实践,而且它并不声称是一本技术参考书,所以这并非不可接受。
例如,命名约定常常不被遵守,所以我们有命名为 displayName
、concatenateName
或 square
的方法,这暗示了这段代码的作者是最近从 Java 转过来的。我不会对此评判,因为我自己也走过同样的路,并且花了一些时间才完全接受并应用这些约定。
不那么无害的是修改值类型实例的状态,这表明对这类类型的细微之处不熟悉。但为作者辩护的是,少数 C#/.NET/WinRT 开发者才意识到这个“问题”。
仍然关于值类型,书中说最好使用引用数组而不是值数组,因为值会被复制。这完全是错误的,因为数组本身就是引用类型,所以通过引用进行操作,因此在两种情况下都没有涉及复制。所以这不是使用“按引用”语义的好理由,尽管默认情况下,我们应该选择它。
至于类的设计,本书将业务数据与纯技术处理(如访问数据库)混合在一起,而不是将它们解耦。
解耦毕竟是基础,在那里不会付出任何代价,所以没有理由不向初学者演示。
但为这本书辩护的是,认证问题在这方面远非典范。
最后,在演示 ADO.NET 时,大多数示例都没有利用 using
块来控制连接和命令对象的范围,尽管这种最佳实践稍后会在专门的侧边栏中讨论。
一些错误
稍微不那么严重的是纯粹的技术错误,例如断言 foreach
循环中的当前元素必须具有正确的类型,这是错误的,而且有点“危险”,也是很多错误的来源:如果类型不正确,foreach
会隐式转换值:例如,double
到 int
。
有些插图是错误的:例如,对于扩展方法,书中提到扩展 Math
类是无用的,因为它不能被实例化,因为它是一个 static class
,这对作者来说真是运气不好,因为这个类更像一个例外而不是规则,所以是一个非常糟糕的例子。
“partial
”类被描述为在同一个程序集中生成多个文件,而它们都被C# 编译器聚合到一个类中,其字节码作为一个整体集成到程序集中。
我们还“学到”可以将值类型的实例设置为 null
,这完全是错误的:null
仅对引用类型实例的引用(和可空类型)有意义。
书中指出值类型的实例总是存储在堆栈上:错了,它们可以内联在堆中引用类型的实例中,但这在社区中是一种普遍的观念,所以我们不能责怪作者。
更具轶事性的是,我们被告知 int
类型可以存储高达 4 294 967 286 的值,而限制是它的一半。
更令人恼火的是,但再次是一个普遍的信念,本书指出有 2 种传递参数的方式:值类型按值复制,引用类型按引用传递,听起来不错但错误,所有操作都是按值复制,包括引用类型的引用复制,如果你想按引用传递,你必须使用专门的 ref
和 out
标记。
但更特别的是,“abstract
”方法会被称为“virtual
”方法的同义词,而它们是两个相关但不同的概念:abstract
方法可以被认为是 virtual
,但反之不然。所以这可能是对面向对象编程基本概念理解的问题,这证实了我对强烈的 Java 偏见的假设:Java 开发者是使用虚方法最多的人,但也是最不了解它们以及它们与 abstract
方法(相对于 C++ 和 C# 开发者)的区别的人。矛盾吗?现在我们知道在 Java 中,默认情况下所有实例方法都是虚拟的,因此 Java 开发者永远不需要编写“virtual
”这个词,而 C++ 和 C# 开发者必须明确说明。
一些近似值
我同意一些简化对新手无害,可以避免一次性抛出太多信息或让他们产生疑问,但我认为理想情况下,一个人不应该仅仅为了贴纸而准备认证,更重要的是为了确认真正的专业知识,并学习新事物,这需要一些技术准确性。
这是一个无害错误的例子,表明对语言缺乏完全理解:for
循环的最后一部分被描述为执行表达计数的指令(++
、*=
、-=
……),这是错误的,它甚至可以缺失。
当然,许多有才华的开发者可以拥有辉煌的职业生涯,即使他们相信这一点,也不会对他们的工作质量和生产力造成任何后果。
同类型的另一个例子是将 while
和 do-while
循环描述为两个完全独立的实体,而它们是同一枚硬币的两面。
同样,在首次介绍扩展方法时,书中说其目的是在不重新编译类的情况下扩展类,这只是一个相对无关紧要的观点。
幸运的是,在这种情况下,正确的定义在另一个章节中给出,因此完整阅读本书的重要性。
在比较 for
循环和 foreach
循环时,本书指出后者是在我们不知道元素数量时使用的,而这更多是索引器的缺失将决定 foreach
的使用,确实有些集合暴露了它们的长度但没有提供索引器,因此最适合用 foreach
进行迭代。
拥有如此丰富经验的开发者使用这种将 IDE 和平台混淆的近似说法是可惜的:书中说 Visual Studio 调用构造函数,而这当然是 <string>CLR 的职责,Visual Studio 甚至不编译代码。
不幸的是,不求甚解且从未在 Visual Studio 之外进行开发的开发者不明白,当我们开发这类应用程序时,我们有三个参与者:
- .NET 或 WinRT 是底层平台,提供类型库和运行时环境(对于 .NET)
- C# 是一种编程语言,使用编译器生成二进制代码,例如在 .NET 平台上运行的字节码
- Visual Studio 是一种生产力工具(恕我直言,最好的),它通过 MSBuild 等工具与 C# 和 .NET/WinRT 接口。
根据本书的说法,“volatile
”标记表示数据可能被我们的代码之外的其他组件修改,这只是部分正确,因为它在此上下文之外也有效(参见我的另一篇文章:《同步、内存可见性与泄漏抽象》)。
再一次,作者被原谅了,因为这是通过实践一些并行编程才能理解的微妙之处,而且大多数 .NET 和 WinRT 开发者完全可以忽略它进行开发,这是一件好事。
同样,作者似乎不知道 &
也是一个有效的布尔运算符,与 &&
的区别在于它不会短路。不幸的是,其中一个测试题就是关于这个问题,所以这个测试是错误的,这很讽刺,因为书的开头有很长的篇幅介绍认证考试是如何设计的,其中一个前提就是它们必须没有歧义。
有些主题似乎理解不透彻,例如 ORM 被描述为图形工具,而这根本不是它们的主要用例,Entity Framework 通过“Code First”工作流的 U 型转弯就证明了这一点。
至于 NHibernate,据说它是一个专门用于其他数据库和语言的 ORM,而它的第一个用例是从 C# 与 SQL Server 交互。
一些词语误用
通常,词语误用并不严重,但对于需要记住严格定义以获得参考点以帮助他们学习和巩固知识的新手来说,则不然。
例如,System.Int32
(C# 中的别名 int
)被称为一个类而不是简单的类型(为了避免谈论值类型),这可能是在Java及其Integer类型(确实是一个引用类型)的遗留。
关于 C# 结构,定义了一些字段,但被称为“属性”,这会混淆初学者,因为在本书后面,属性本身当然也会用这个词来指代。
通常,本书使用“类文件”而不是“类”,这可能是Java的另一个遗留,它确实只允许每个源文件有一个 public
类,但即使在 Java 中,这也不是一个准确的术语。
同样,本书谈论的是接口实例而不是接口引用,但是接口不能被实例化,所以这再次混淆了初学者。
另一个未完全掌握的主题是 BackgroundWorker
:在事件处理程序 ProgressChanged
和 WorkerCompleted
中使用了 Invoke
方法,而 BackgroundWorker
的目的正是通过自动捕获上下文并直接在UI 线程上调用处理程序来避免这种情况。
一些小问题
正如你所预料的,这本书明显缺乏校对:有大量的拼写错误和语法错误,但幸运的是,它们从未影响理解,上下文总能帮助读者领会内容。
在本书的末尾,有大量代码用于演示验证和Windows Forms。
首先,我不明白倾倒如此大量代码的目的,其次,它集中了许多不良实践:玩弄浮动值而不是将它们整合到专用类型中,业务代码直接放在 UI 事件处理程序中而不是隔离到专用组件中,控件的背景颜色用于存储业务数据,窗体的验证状态,这是一种奇怪的做法。
再次,危险在于新手可能会学到很多不良实践并将其应用到他们的专业环境中,甚至教给他们的同事。
一些技术术语在某些章节的词汇表中定义,但从未被提及,这可能意味着某些部分被简单地“遗忘”了。
一些主题重复出现:关于委托、匿名函数和lambda表达式的章节出现了两次,文本内容不同,因此可能由两位作者在未经协调的情况下完成。
过多的时间花在了像 ArrayList
这样已经过时的组件上,现在它们已被相应的泛型类所取代。
好的部分
只列出错误是不公平的,因为这本书充满了非常好的内容,有些章节质量很高,堪比最好的技术参考书。
一些好的介绍
首先,本书介绍了使用 C# 在 .NET 或 WinRT 中开发的工具概览,许多章节都是简短的介绍,让读者快速理解给定工具的来龙去脉。
对并行编程的介绍非常正确,包括对线程池及其限制(如无法进行 join
)的简短概述,以及对 C# 5.0 中异步方法的概述。
关于监视器的部分非常适合复习,但对于初次接触该主题的初学者来说有点太短,他们可能无法理解某些概念,例如“就绪队列”。
本书还对 Entity Framework 进行了很好的介绍,并用“Database First”工作流演示了它的使用,这是 EF 支持的三种工作流之一。
LINQ 也得到了很好的介绍,并且展示了构建查询的两种语法:通过集成到 C# 中的迷你语言或直接通过 Enumerable
静态类提供的方法链。
对安全性的介绍非常广泛而紧凑,涵盖了各种主题,如哈希、对称和非对称加密,以及证书等。
对于像我这样“了解”这些主题但并不完全清楚如何使用 .NET/WinRT 实现它们的开发者来说,这很完美。
顺便说一句,我在认证考试中就这部分有一个问题。
一些好的插图
例如,do-while
循环的使用通过一个相关且反复出现的用例进行了说明:从控制台读取输入。
同样,本书为枚举器的使用提供了一个非平凡的示例,这有助于读者理解 foreach
循环的底层工作原理。
关于面向对象编程,也有很好的例子,特别是在抽象类和虚方法的使用上,以及契约和接口的概念(例如,通过显式实现)。
为了说明反射的重要性,本书使用了对象关系映射的上下文,这除了是一个相关的用例之外,对新手来说也是一个很棒的技术演示,因为在 20 行代码中,我们有一个迷你<string>ORM,而乍一看,我们可能会预期一个更复杂的实现。
一些涵盖得很好的主题
一些主题得到了更好的关注,并包含了一些真正全面的技术元素。
字符串、其不变性以及 CLR 管理的存储(字符串驻留)都得到了很好的阐述。
对垃圾回收内部工作原理的介绍简短而高质量,尤其强调非确定性,并用文件使用来加以说明。
这个例子帮助读者自然地理解终结器/析构函数和可处置模式的目标。
解释是均衡的:有足够的技术深度来正确理解内部工作原理并获得超出认证范围的知识,同时又不会用过多的细节让新手不知所措,同时揭示了原理和用例。
这是我读过的关于这个主题最好的描述之一。
另一个内容比预期认证内容更好更丰富的例子是 CodeDOM,它受益于使用 C# 和 VB.NET 的完整而丰富的演示,超越了网络和书籍上到处复制粘贴的经典示例,即编译一个 C# 的“Hello world
”程序。
一些情境化和良好实践
最后,你需要知道这本书提供了大量信息,帮助读者理解事物,并提供了一些良好实践。
即使这对于认证来说并不是真正有用(认证主要评估你使用工具的能力,而不是谈论它们),它也会帮助每个开发者更好地理解他们使用的某些工具,而无需真正了解它们。
例如,枚举类型的原理得到了很好的解释,强调了代码可读性的提升,从而提高了可维护性。
同样,对于泛型类型和方法,本书强调了它们的所有优点,例如代码可重用性、类型安全和性能。
封装概念的介绍非常好,引用验证和使用转换来避免 SQL 注入作为相关用例,后者不太现实,但很有趣。
本书强调良好实践,例如即使只有一条指令也要系统地使用大括号,典型的是 if
块。
经常有一些好的建议,例如不要通过异常暴露敏感信息的重要性,如果它们必须跨越应用程序域的边界,则应使其可序列化。
最后,本书推荐了优秀的附加资源,例如pinvoke.net,这在使用 P/Invoke (DllImport
) 进行本机互操作时几乎必不可少。
综合评估
这本书的主要优点在于它涵盖了广泛的主题,恕我直言,它完全符合认证官方大纲的要求。
在认证过程中,我没有遇到一个问题是这本书中没有讨论过的主题。
根据读者在 C#/.NET/WinRT 方面的当前技能,这本书对她/他的帮助程度会有所不同
- 如果你非常强,是拥有至少 5 年经验的高级开发者,那么你将能够去伪存真(从错误中发现精髓),这本书将是你通过认证的最佳盟友之一
- 如果你是中级水平,基础扎实,这本书可能会帮助你达到更高的水平,因为它列出了你需要掌握的重要技术主题
- 但如果你是初学者,姑且不论现在通过认证可能为时尚早,如果你天真地学习一些错误和不良实践,这本书可能会很危险,所以你必须加倍警惕,如有疑问,请毫不犹豫地向经验丰富的开发者请教。
对我来说,我把这本书的内容分为以下几部分:
- 75% 的内容是我已知,书本帮助我复习的部分,
- 15% 的内容是我已经忘记的主题,我很高兴花时间完整阅读了这本书,因为错过 15% 的考纲内容很多,这相当于你本次考试(必须至少达到 700/1000 分)一半的误差范围,
- 10% 的内容是我不知道的主题,我可能听说过,但从未使用过,例如 WCF Data Service 或在正则表达式中内联选项的可能性,或者我错过的一些最佳实践,例如将标签与
#endregion
指令一起使用。
所以我通过阅读这本书学到了很多,有些事情不如其他事情重要,但当你追求专业知识时,每一份知识都值得学习。
结论
如你所见,《MCSD 认证工具包 (考试 70-483):C# 编程》的内容参差不齐。
我不明白的是,尽管有许多强烈(恕我直言,往往有些不成比例)的读者感到被冒犯和失望的反应,电子书版本为何仍然有如此多的错误。
那么,你应该阅读这本书吗?
如果你想通过 70-483 认证,答案是肯定的,因为这本书正确地总结了你需要了解的所有技术要点才能成功,它将帮助你发现需要弥补的弱点。
这也是一个默认的选择,因为确实没有其他替代品。
然而,如果你需要一本技术书籍来发展你现有的技能并获得新技能,那么我不推荐这本书,因为你可能会学到一些不良实践,而且带着怀疑的态度阅读一本书对记忆非常不利,因为我们往往什么都不相信。
最重要的是,不要像其他读者那样,因为错误的累积而读了几页或几章就放弃,原因有二:
- 这本书的质量确实一章比一章好,我在前半部分发现了很多错误,但到结尾几乎没有发现任何错误,仿佛前半部分是由一个新手开发者写的,中间部分是由一个更有经验的开发者写的,最后一部分则是由一个专家写的。
- 即使在错误百出的部分,也有一些重要的信息,甚至是一些宝藏。
所以,即使你被认为是专家,这本书也会教你一些东西,不读它去通过认证会很可惜。
可以肯定的是,这本书是大量工作的成果,值得我们高度尊重,有不完善的资源总比没有资源好。
我的最终评分
给这本书评分相当困难,因为一方面,我认为技术错误和近似值的累积对新手读者(他们可能占这本书读者群的重要部分)真的有害,但另一方面,这本书完美地完成了帮助读者准备认证的工作。
如果它是一本自称是技术参考书的书,我会给它非常严厉的评价:5/10。
但对于只想获得认证的读者来说,它完全值得 9/10。
所以我最终给它打了 8/10 分,亚马逊上是 5 星中的 4 星,因为我认为通过认证确实是这本书的重点,它不是一本技术参考书,所以我过分强调了这方面。