关于我们
![]() ![]() |
像C#高手一样编程
本书将会讨论一些C#编程重要的后端技能,并且在典型情形(重构一个代码库,以确保其安全,整洁和可读)中练习应用这些技能。本书包括6部分,第1部分位基础知识讲解,第2部分为代码设计和改进,第3部分为访问数据库,第4部分为存储模式,第5部分为服务层讲解,第6部分为控制器类讲解。在完成这些内容和练习之后,读者将对C#语言具有更深刻的理解,并且可以准备学习更高水平的内容。
我第一次接触C#语言是2016年在富士胶片医疗系统公司工作时。我之前曾使用过Java和Python,但自从开始使用C#语言,我便沉迷其中不能自拔。我喜欢它的入门容易以及清晰的编写方式(尽管最初是极度令人气愤的)的特点。当时,在公司工作期间,我多次向我的同事咨询有关C#的问题。入门虽然很容易,但是达到熟练使用却是另外一回事。例如,每个人,无论知识背景如何,都可以在10分钟之内编写一个“Hello, World!”程序,但是充分应用一门编程语言的最大功能以及明白为什么却需要相当长的时间。学习和使用一段时间之后,我很快遇到了瓶颈,于是就开始寻找能够提升我C#水平的资料。我发现与.NET和C#相关的书籍有三种类型:第一种是超出语言本身的主题(整洁代码,架构,基础),却恰巧使用了C#语言的书籍;第二种是有关如何使用C#入门编程的书籍;第三种是非常高级的,你阅读之后可能都有资格成为微软公司CTO的书籍。而我希望有一本书可以介于这三种类型之间,即一本可以从处理代码开始,能引领读者从初步认识逐步了解高级主题的书,而市场上这样的书之前并不存在,因此我就写了本书。 如果您是软件工程师(开发者、码农或者其他),之前具有一定的编程经验(最好是面向对象的),并且想要更深入地学习C#,那么这本书非常适合您。您不必学习怎样编写一个条件语句,本书也不会向您解释什么是对象。这本书中包含的技能和主题,都会帮助读者深入学习C#语言及其平台。当然,我不能保证本书中的每一个知识点都是新知识,但是在这本书有限的内容中,会尽可能尝试多讲解新知识。非常希望你们能够喜欢这本书,并且能够从中学到一些新知识,有新的收获。当然,如果本书中提到的知识都是您所熟知的,那么复习一遍也是不错的。致谢 当第一次与Manning出版社讨论关于编写这本书的时候,我其实并没有意料到它会占用我一年的时间。实际上,我曾经被多次提醒作者通常会低估写书所消耗的时间,而我固执地认为我会是例外的那一个,我确实没有成为例外。从2019年12月到2021年3月,我写这本书中投入了许多的时间,我曾多次想:“这次肯定能结尾”,但是除了最后一次,都没能成功结尾。 幸运的是,我有一位非常耐心的妻子,她一直在陪伴我,支持我。因此我首先要感谢我的妻子,没有她坚定不移的支持,我不可能完成这本书,她是我这本书诞生的基石。我还要感谢其他家人,他们总是关心我的进展。在本书的商业案例中,我将外祖父的名(Aljen)与祖母的姓(van der Meulen)结合起来,作为公司CEO姓名。 我还要感谢Manning出版社的朋友。特别感谢Marina Micheal,她作为本书的编辑,为本书出版做了很多工作,多亏了Marina使我现在不太敢在编写书时使用will这个词。我还有一个由JeanFranois Morin, Tanya Wilke, Eric Lippert, Rich Ward, Enrico Buonanno和Katie Tennant组成的重量级团队。这个由超级英雄/忍者/摇滚明星组成的洲际团队成员为本书反馈了很多意想不到的意见,以及技术错误。我还要感谢所有在出版之前阅读本书草稿并给出细致反馈的审核人员,他们对本书的细小错误也毫不含糊,你们的努力付出使得这本书质量更好。虽然我并没有将这本书视为自己的杰作,但是我希望读者可以从中学到一些尽可能多且有用的知识。 感谢所有的审核人员:Arnaud Bailly, Christian Thoudahl, Daniel VásquezEstupian, Edin Kapic, Foster Haines, George Thomas, Goetz Heller, Gustavo FilipeRamos Gomes, Hilde Van Gysel, Jared Duncan, Jason Hales, JeanFranois Morin, JeffNeumann, Karthikeyarajan Rajendran, Luis Moux, Marc Roulleau, Mario Solomou, Noah Betzen, Oliver Korten, Patrick Regan, Prabhuti Prakash, Raymond Cheung, Reza Zeinali, Richard B. Ward, Richard DeHoff, Sau Fai Fong, Slavomir Furman, Tanya Wilke, Thomas F. Gueth, Víctor M. Pérez和Viktor Bek。 最后,我还要感谢几个人,他们不仅在我的职业生涯中扮演着非常重要的角色,还帮助我完成了本书的部分编写工作。首先是David Lavielle和Duncan Handerson,感谢你们给我机会,为我提供了第一份软件开发工作;然后是Jerry Finegan,感谢您带我了解C#语言,并为我解答疑惑,非常感谢您的耐心和回复。Michael Breecher:感谢您参与编写本书中有关的内容(在大半夜回复我一些有关符号的奇怪数学问题),这本书因为您的参与而变得更完美。Szymon Zuberek,第二章的初稿是在您纽约的公寓里完成的,非常感谢您提供了很多聊天话题。我还要感谢Acronis和Workiva公司的优秀同事,他们常常认真听我絮叨这本书,他们真的很有耐心。致谢像C#高手一样编程本书内容简介 这本书内容是基于现有的编程技能,又可帮助大家进一步提高编程实践技能,或者帮助您从对Java或其他面向对象的编程语言认知过渡到对C#认知。本书将教您学习如何编写对于企业开发至关重要、地道的C#代码。书中会讨论一些重要的后端方面的技能,以及在典型情形(重构一个代码库,以确保其安全、整洁和可读)中如何练习并应用这些技能。在完成书中这些练习之后,您将对C#语言具有更深刻的理解,并且具有学习更高水平内容的基础。 本书中并不会介绍“Hello,World!”或者计算机基础,而是会引导大家通过重构一个过时的代码库来进行学习,教您尝试采用新技术、新工具和最佳实现方式将这个代码库升级到现代C#的标准。在本书中,我们将以一个现有的(.NET框架下编写的)代码库为例,使用简化的API将其重构为.NET5框架。 本书读者群体 如果您是一名精通面向对象编程语言的开发人员,无论您会的是Java、Dart、C++,还是其他语言,本书都可以帮助您快速掌握C#和 .NET。您之前拥有的知识完全可以应用上,不必花费时间重新学习编写一个if语句 同样,如果您精通一些类似于Go、C、JavaScript、Python或其他一些主流编程语言,通过认真学习本书内容,您将能够编写完整、地道的C#代码。如果您想要学习一些有关面向对象的设计知识,学习C#也没有坏处(提示一点,如果您之前使用的是Go语言,请在使用接口时一定要特别小心,因为它们的工作方式并不相同)。 还有一种情况,如果您已经使用过C#,并且想要知道如何“提升”您的C#知识,这本书会很适合您,本书中有很多学习高级C#的知识,本书将会帮您想要弥补的知识。 本书主要结构 与其他普通技术书籍相比,本书的结构有些非常规。大部分的技术书籍都属于参考性书,读者可以以任何顺序来阅读,而这本书并不是参考书,需要按照顺序阅读。如图01所示,本书围绕以下6个部分构成: (1) “C#和 .NET”,在第1章中会讨论这本书是什么样的书,这本书会教什么,不教什么。第2章主要是对C#语言和.NET生态的简单回顾,重点介绍.NET的优越性,以及C#是如何编译的。 (2) “现有代码仓库”,在这部分,会指导探索我们目前已有的代码库。这部分介绍现有代码库,并讨论代码库的设计缺陷和潜在改进方案。 (3) “数据库访问层”,这部分开始重写整个服务。在第3部分中,将专注于介绍创建全新的 .NET Core项目,并教大家学习如何使用Entity Framework Core连接到云(或本地)数据库。另外,本部分讨论的内容还包括存储/服务(repository/service)模式、虚方法(virtual method)、属性(property)和密封类(sealed class)。 (4) “存储层”,在第4部分,将教大家进一步了解存储/服务模式,如何实现五个存储库类。您将了解到依赖注入(dependency injection)、多线程(包括lcok锁、mutex互斥锁和semaphore信号量)、自定义相等比较(custom equality comparison)、测试驱动开发(testdriven development)、泛型(generic)、扩展方法(extension method)和LINQ这些知识。 (5) “服务层”,就是实现服务层的类。在这部分中,将介绍从头开始时编写的服务层,讨论反射(reflection)、模拟(mocking)、耦合(coupling)、运行时断言(runtime assertion)、类型检查(type check)、错误处理(error handling)、结构体(struct)和生成式返回(yield return)。 (6) “控制器层”,第6部分是重写代码仓库的最后一步。在这部分中,将介绍如何编写两个控制器类,以及执行验收测试。除了这些内容,还将介绍 ASP.NET Core中间件(middleware)、HTTP路由、自定义数据绑定、数据序列化和反序列化,以及在运行时生成的OpenAPI说明书。 本书的很多章节(包括章节中的很多小节)中,都包含了测试您知识水平的小练习,这些练习可以很快地完成。我真诚建议您完成这些练习时,同时记得复习您觉得自己理解不透彻的知识点。 图1流程图(全书)图1为建议阅读本书的流程图。按照这个流程图阅读本书,认真学习,可以达到理想的阅读效果。该流程图的灵感来自The Art of Computer Programming series(Donald Knuth)一书。 本书主要情况简介像C#高手一样编程本书中介绍的相关代码 在编写代码的时候,可以将.NET分为3个主要分支:.NET框架4.x,.NET Core3.x和.NET5。除了第3章和第4章(阅读之后您会明白为什么),本书均以.NET5为对象进行编写。 本书以C#语言版本中C#3和C#9为例进行讲解(如您使用的是C#8也是可以的)。C#语言是向下兼容的,您只要安装最新版本的C#(在撰写本书时,最新版本为C#8或C#9预览版)对照学习就可以。介绍源代码的章有第2章、第3和4(合并)章、第5章、第6章、第7章、第8章、第9章、第10章、第11章、第12章、第13章和第14章。 如要练习运行这些源代码,需要安装高于3.5版本的.NET框架(如果想要运行第3章和第4章的代码)和.NET5。如果想要“本地运行”本书中所提到的数据库,或是在实际安装时遇到困难,可以参考附录C(“安装指南”)中的安装说明。本书主要使用Visual Studio作为IDE,但是您也可以使用任何自己喜欢的、支持C#语言的IDE。Visual Studio 2019有一个免费的社区版本供我们使用,当必须使用Visual Studio的情况时,本书会有提示。代码和.NET5应当在Windows、macOS或Linux上运行。本书中尽可能使用命令行(对于macOS用户来说是终端),以避免大家依赖于任何特定的IDE或操作系统。 本书中有很多源代码示例,它们有可能是带有编号的代码示例,也有可能是与常规文本混合在一起的,这两种情况,源代码都采用了固定宽度字体,以将其与普通文本区分开。有时代码名称还会加粗,以突出显示在之前步骤基础上发生修改的代码,比如将新功能添加至现有代码行时,会将新功能名称加粗。 在很多情况下,源代码已经被重新格式化,添加了换行符并修改了缩进,这是为了以尽可能符合书籍的排版。还有些处理得仍然还不够好,代码示例中还会包含行延续标记()。本书中很多代码示例中都包含了代码注释,用以强调重要的概念。另外,请大家注意,新代码块的大括号通常被放置在前一行上,这并不符合C#在实际应用中的习惯,但是在本书中,这样做可以有效节约空间,本书提供的源代码本身并不会使用这一形式。 与本书相关的论坛 购买本书的读者可以免费访问Manning出版社运营的网络论坛,您可以在论坛上发表关于本书的评论,询问技术问题,以获得帮助。如要访问论坛,请登录https://livebook.manning.com/book/codelikeaproincsharp/welcome/v9/。您还可以在https:// livebook.manning.com/#!/discussion中了解更多有关Manning论坛的信息以及行为准则。 Manning出版社仅为读者提供一个环境平台,让读者与读者,读者与作者能够方便沟通。对于论坛,作者并没有承诺在论坛中的参与程度,其对论坛的贡献是自愿和无偿的。我们希望读者尽可能询问一些具有建设性的问题,这样作者也更有兴趣解答。本书一经出版,出版社的论坛和相关资料都可以访问和查询。
Jort Rodenburg是一名专门从事C#的软件工程师,他为许多领域的应用构建软件。同时,Jort还是一名教授,他指导并教授面向对象编程的课程,以帮助开发人员掌握C#和.NET。
第1部分使用C#和 .NET第1章C#和.NET相关概念及使用说明3 1.1 C#的优势4 1.1.1 C#的经济性5 1.1.2 C#的可维护性5 1.1.3C#的易于操作性6 1.2为什么有时不使用C#7 1.2.1操作系统开发7 1.2.2嵌入式开发7 1.2.3数值计算8 1.3C#使用入门8 1.4本书涉及C#的主要内容10 1.5本书不涉及的C#的内容11 1.6总结12 第2章.NET及其编译13 2.1.NET框架14 2.2.NET514 2.3兼容CLI的语言的编译15 2.3.1C#代码(高级语言)的编译17 2.3.2通用中间语言(汇编层)的编译19 2.3.3本地代码(处理器层)的处理26 2.4练习27 2.5总结28 第2部分现有代码库第3章这个代码有多糟糕33 3.1飞翔荷兰人航空公司简介34 3.2应用代码库的基本要求35 3.2.1映射对象关系35 3.2.2GET/flight 终端地址——检索所有航班的信息36 3.2.3GET/flight/ 终端地址——获取特定航班信息37 3.2.4POST/booking/终端地址——预定航班38 3.3保存部分现有代码40 3.3.1评估现有数据库架构及数据表40 3.3.2现有代码库的网络服务配置文件41 3.3.3查看现有代码库中的模型和视图47 3.4总结54 第4章非托管资源管理55 4.1FlightController——GET/flight56 4.1.1GET/flight终端地址以及它的功能56 4.1.2方法签名——响应类型的含义和typeof58 4.1.3使用集合收集航班信息60 4.1.4使用连接字符串的弊端61 4.1.5使用IDisposable释放托管资源61 4.1.6使用SqlCommand查询数据库63 4.2FlightController: GET/flight/{flightNumber}66 4.3FlightController: POST/flight69 4.4The FlightController: DELETE/flight/{flightNumber}74 4.5练习75 4.6总结76 目录像C#高手一样编程第3部分数据库访问层第5章使用Entity Framework Core设置项目和数据库79 5.1创建.NET5解决方案和项目80 5.2设置和配置网络服务84 5.2.1配置一个.NET5网络服务84 5.2.2创建和使用HostBuilder86 5.2.3创建Startup文件89 5.2.4使用存储/服务模式作为网络服务架构92 5.3构建数据库访问层93 5.3.1Entity Framework Core和逆向94 5.3.2DbSet和Entity Framework Core工作流程96 5.3.3配置方法和环境变量97 5.3.4Windows中环境变量设置98 5.3.5macOS中环境变量设置99 5.3.6通过代码检索运行环境变量99 5.4练习102 5.5总结103 第4部分存储层第6章测试驱动开发和依赖注入107 6.1测试驱动开发108 6.2CreateCustomer方法112 6.2.1验证输入参数113 6.2.2使用arrange、act和assert流程编写单元测试114 6.2.3无效字符的验证115 6.2.4具有\[DataRow\]属性的内联测试117 6.2.5对象初始化器和自动生成代码118 6.2.6构造器、反射和异步编程120 6.2.7lock锁、mutex互斥锁和semaphore信号量122 6.2.8同步执行转换到异步执行123 6.2.9Entity Framework Core测试124 6.2.10依赖注入控制依赖关系的使用126 6.3练习132 6.4总结133 第7章对象比较134 7.1GetCustomerByName方法135 7.1.1可空类型及其应用137 7.1.2LINQ和扩展方法137 7.2C#的由来142 7.2.1应用EqualityComparer创建“比较器”类144 7.2.2覆盖Equals测试等价性146 7.2.3重载等价运算符147 7.3练习150 7.4总结151 第8章stub泛型和耦合153 8.1Booking存储库的实现154 8.2关注点分离与耦合156 8.3对象初始化器的使用160 8.4stub单元测试的使用163 8.5泛型编程的使用167 8.6使用可选参数提供默认参数169 8.7条件语句、Func和switch表达式171 8.7.1三元条件运算符171 8.7.2使用函数数组进行分支172 8.7.3switch语句和表达式173 8.7.4Entity Framework Core中的待处理更改查询174 8.8练习177 8.9总结179 第9章扩展方法、流和抽象类181 9.1Airport存储库的实现182 9.2Airport对象的获取——通过机场ID数据库183 9.3AirportID输入参数的验证185 9.4输出流186 9.5Airport对象的获取——通过数据库查询191 9.6Flight存储库的实现199 9.6.1IsPositive扩展方法和“魔法数字”201 9.6.2数据库中航班的获取206 9.7练习209 9.8总结210 第5部分服务层第10章反射和模拟213 10.1回顾存储/服务模式214 10.1.1服务类的用途是什么?215 10.2实现CustomerService216 10.2.1为成功做准备——创建类骨架216 10.2.2删除自己代码的方法217 10.3BookingService的实现219 10.3.1跨架构层的单元测试223 10.3.2stub和mock的区别224 10.3.3Moq库模拟类的使用225 10.3.4存储库调用231 10.4练习233 10.5总结236 第11章运行类型检查回顾和错误处理237 11.1验证服务层方法的输入参数238 11.1.1is和as运算符的运行类型检查241 11.1.2is运算符的类型检查242 11.1.3as运算符的类型检查243 11.1.4验证服务层方法总结244 11.2BookingServiceTests类清理244 11.3服务类中的外键约束247 11.3.1Flight存储库的调用248 11.4练习260 11.5总结261 第12章IAsyncEnumerable和yield return的使用262 12.1程序需要AirportService类吗?263 12.2FlightService类的实现264 12.2.1通过FlightRepository获取特定航班信息264 12.2.2数据流视图组合268 12.2.3trycatch代码块与yield return使用276 12.2.4GetFlightByFlightNumber的实现281 12.3练习287 12.4总结288 第6部分控制器层第13章中间件、HTTP路由以及其响应293 13.1存储/服务模式中的控制器类294 13.2决定要实现的控制器类别296 13.3FlightController的实现297 13.3.1HTTP响应(GetFlights)的返回298 13.3.2使用中间件将依赖项注入控制器301 13.3.3GET/Flight/{FlightNumber}终端地址实现310 13.4将HTTP请求路由至控制器和方法314 13.5练习319 13.6总结320 第14章 JSON序列化/反序列化以及自定义模型的绑定321 14.1BookingController类的实现322 14.1.1数据反序列化简介323 14.1.2使用\[FromBody\]特性反序列化传入的HTTP数据327 14.1.3自定义模型绑定器及其对应方法特性的使用328 14.1.4CreateBooking终端地址方法逻辑的实现332 14.2验收测试及添加Swagger中间件337 14.2.1OpenAPI的手动验收测试338 14.2.2生成OpenAPI规范342 14.3结束语348 14.4总结348 附录A练习答案349 附录B整洁代码检查表355 附录C安装指南357 附录DOpenAPI360 附录E阅读列表363
你还可能感兴趣
我要评论
|