描述
开 本: 16开纸 张: 胶版纸包 装: 平装-胶订是否套装: 否国际标准书号ISBN: 9787302668046
本书第8版吸取本书以往各版本之精华,深入探讨了C# 12.0的主要功能,比如类型别名、内联数组、默认Lambda表达式参数以及对许多主要语言构造类型的扩展支持。
书中全面透彻地讲解C#语言,旨在帮助广大读者快速掌握有价值的新特性,进一步成长为优秀的C#高级程序员。书中采用简洁的例子来阐释核心概念,同时还重点强调了针对不同水平的读者提供了大量现代设计规范,力求帮助读者减少错误,写出更高质量的代码。
通过本书的阅读,读者将获得以下提升:
?运用结构化编程,快速编写可实际工作的代码
?了解可空引用类型的复杂性及其解决方案
?全面掌握C#对象的构造,包括类、继承和接口
?巧用泛型、委托和Lambda表达式减少代码冗余
?通过LINQ来实现对集合的充分运用
?通过基于任务的异步模式和异步流来改进多线程处理
?通过并行数据处理和多线程任务来提升性能
?充分利用反射、属性和宣告式编程范式
?用增强的模式匹配语法来实现复杂类型的编程
?巧用关键字record struct和record class编写类型定义
?探索和比较C#8.0到C#12.0各个版本的新特性
《C# 12.0本质论(第8版)》是C#领域中广受好评的经典著作。作为C#权威指南,本书深入解析了C# 12.0的类型别名、内联数组、默认Lambda表达式参数以及对许多主要语言构造的扩展支持。全书共24章,每章开头用“思维导图”指明要讨论的主题以及每个主题与整体的关系。在介绍相关知识点的同时结合了大量通俗易懂的实例,旨在帮助读者全面掌握C#语言,快速成为C#高级程序员。
《C# 12.0本质论(第8版)》适用于对C#感兴趣的各种层次的读者,无论是初学者还是资源开发人员,都可以从本书中获益。
第1章 C#概述 001
1.1 Hello, World 002
1.1.1 创建、编辑、编译和运行C#源代码 002
1.1.2 理解项目 006
1.1.3 编译和执行 007
1.1.4 使用本书源代码 008
1.2 C#语法基础 008
1.2.1 语句和语句定界符 008
1.2.2 认识类和方法 009
1.2.3 C#语言的关键字 010
1.2.4 标识符 011
1.2.5 类型定义 013
1.2.6 Main方法 014
1.2.7 空白 015
1.3 使用变量 016
1.3.1 数据类型 016
1.3.2 变量声明 017
1.3.3 变量赋值 017
1.3.4 使用变量 018
1.4 控制台输入和输出 019
1.4.1 从控制台获取输入 019
1.4.2 将输出写入控制台 020
1.4.3 注释 022
1.4.4 调试 024
1.5 托管执行和CLI 025
1.6 多个.NET框架 029
1.6.1 应用程序编程接口 030
1.6.2 C#和.NET版本控制 030
1.7 小结 032
第2章 数据类型 033
2.1 类型名称形式 033
2.2 基本数值类型 035
2.2.1 整数类型 035
2.2.2 浮点类型 036
2.2.3 decimal类型 037
2.2.4 字面值 038
2.3 更多基元类型 042
2.3.1 布尔类型bool 042
2.3.2 字符类型char 043
2.3.3 字符串string 044
2.3.4 关键字null和void 055
2.4 数据类型转换 056
2.4.1 显式转型 057
2.4.2 隐式转型 058
2.4.3 不使用转型操作符的类型转换 059
2.5 小结 061
第3章 深入数据类型 062
3.1 类型的划分 062
3.1.1 值类型 062
3.1.2 引用类型 063
3.2 声明允许为null的类型 064
3.2.1 对null引用进行解引用 064
3.2.2 可空值类型 065
3.2.3 可空引用类型 066
3.3 隐式类型的局部变量 067
3.4 元组 068
3.5 数组 073
3.5.1 数组声明 075
3.5.2 数组实例化和赋值 076
3.5.3 使用数组 079
3.5.4 范围 083
3.5.5 更多数组方法 084
3.5.6 数组的实例成员 085
3.5.7 字符串作为数组 086
3.5.8 常见数组错误 088
3.6 小结 089
第4章 操作符和控制流 090
4.1 操作符 091
4.1.1 一元正负操作符 091
4.1.2 二元算术操作符 092
4.1.3 复合赋值操作符 097
4.1.4 递增和递减操作符 098
4.1.5 常量表达式和常量符号 101
4.2 控制流概述 102
4.2.1 if语句 104
4.2.2 嵌套if 104
4.3 代码块({}) 106
4.4 代码块、作用域和声明空间 107
4.5 布尔表达式 109
4.5.1 关系操作符和相等性操作符 109
4.5.2 逻辑操作符 110
4.5.3 逻辑取反操作符! 112
4.5.4 条件操作符?: 112
4.6 用null编程 113
4.6.1 检查是否为null 113
4.6.2 空合并操作符??和空合并赋值操作符??= 115
4.6.3 空条件操作符?. 116
4.6.4 空包容操作符! 117
4.7 按位操作符 119
4.7.1 移位操作符 120
4.7.2 按位操作符 120
4.7.3 按位复合赋值操作符 122
4.7.4 按位取反操作符 123
4.8 再论控制流语句 123
4.8.1 while循环和do/while循环 123
4.8.2 for循环 125
4.8.3 foreach循环 127
4.8.4 基本switch语句 129
4.9 跳转语句 132
4.9.1 break语句 132
4.9.2 continue语句 133
4.9.3 goto语句 135
4.10 C#预处理器指令 136
4.10.1 排除和包含代码 137
4.10.2 定义预处理器符号 138
4.10.3 生成错误#error和警告#warning 139
4.10.4 关闭警告消息#pragma 139
4.10.5 nowarn:选项 140
4.10.6 指定行号#line 140
4.10.7 可视编辑器提示#region和#endregion 141
4.11 小结 142
第5章 参数和方法 144
5.1 调用方法 145
5.1.1 命名空间 146
5.1.2 类型名称 148
5.1.3 作用域 148
5.1.4 方法名称 148
5.1.5 形参和实参 148
5.1.6 方法返回值 148
5.1.7 对比语句和方法调用 149
5.2 声明方法 149
5.2.1 参数声明 151
5.2.2 方法返回类型声明 151
5.2.3 表达式主体方法 153
5.2.4 本地函数 154
5.3 using指令 154
5.3.1 using指令概述 155
5.3.2 隐式using指令 156
5.3.3 全局using指令 157
5.3.4 .csproj Using元素 158
5.3.5 using static指令 159
5.3.6 使用别名 160
5.4 Main()方法的返回值和参数 161
5.5 顶级语句 163
5.6 高级方法参数 164
5.6.1 值参数 164
5.6.2 引用参数ref 166
5.6.3 输出参数out 167
5.6.4 只读传引用 169
5.6.5 返回引用 169
5.6.6 参数数组(params) 171
5.7 递归 173
5.8 方法重载 175
5.9 可选参数 178
5.10 用异常实现基本错误处理 181
5.10.1 捕捉错误 182
5.10.2 使用throw语句报告错误 189
5.11 小结 194
第6章 类 195
6.1 类的声明和实例化 198
6.2 实例字段 200
6.2.1 声明实例字段 201
6.2.2 访问实例字段 202
6.3 实例方法 202
6.4 使用this关键字 204
6.5 访问修饰符 209
6.6 属性 211
6.6.1 声明属性 212
6.6.2 自动实现的属性 213
6.6.3 属性和字段的设计规范 215
6.6.4 提供属性验证 216
6.6.5 只读和只写属性 218
6.6.6 计算属性 219
6.6.7 取值和赋值方法的访问修饰符 221
6.6.8 属性和方法调用不允许作为ref或out参数值 222
6.7 构造函数 223
6.7.1 声明主构造函数 224
6.7.2 定义构造函数 225
6.7.3 默认和拷贝构造函数 225
6.7.4 对象初始化器 226
6.7.5 仅初始化的赋值函数 228
6.7.6 重载构造函数 229
6.7.7 构造函数链:使用this调用另一个构造函数 230
6.8 在构造函数中初始化非空引用类型的属性 232
6.8.1 可读/可写非空引用类型的属性 233
6.8.2 只读自动实现的引用类型属性 234
6.8.3 required修饰符 235
6.9 可空特性 238
6.10 解构函数 240
6.11 静态成员 242
6.11.1 静态字段 242
6.11.2 静态方法 244
6.11.3 静态构造函数 246
6.11.4 静态属性 247
6.11.5 静态类 248
6.12 扩展方法 249
6.13 封装数据 251
6.13.1 const 251
6.13.2 readonly 252
6.14 嵌套类 253
6.15 分部类 255
6.15.1 定义分部类 256
6.15.2 分部方法 257
6.16 小结 259
第7章 继承 260
7.1 派生 261
7.1.1 基类型和派生类型之间的转型 263
7.1.2 private访问修饰符 265
7.1.3 protected访问修饰符 266
7.1.4 扩展方法 267
7.1.5 单继承 267
7.1.6 密封类 268
7.2 重写基类 268
7.2.1 virtual修饰符 268
7.2.2 协变返回类型 271
7.2.3 new修饰符 271
7.2.4 sealed修饰符 274
7.2.5 base成员 275
7.2.6 调用基类构造函数 276
7.3 抽象类 278
7.4 所有类都从System.Object派生 282
7.5 类型检查 283
7.5.1 使用is操作符进行类型检查 284
7.5.2 用声明进行类型检查 285
7.5.3 使用switch语句进行类型检查 286
7.5.4 使用switch表达式进行类型检查 286
7.6 模式匹配 287
7.6.1 常量模式(C# 7.0) 287
7.6.2 关系模式(C# 9.0) 288
7.6.3 逻辑模式(C# 9.0) 289
7.6.4 圆括号模式(C# 9.0) 290
7.6.5 元组模式(C# 8.0) 290
7.6.6 位置模式(C# 8.0) 291
7.6.7 属性模式(C# 8.0和C# 10.0) 292
7.6.8 when子句 294
7.6.9 使用无关类型进行模式匹配 294
7.6.10 递归模式匹配(C# 7.0) 295
7.6.11 列表模式 296
7.7 能利用多态性就避免模式匹配 298
7.8 小结 300
第8章 接口 301
8.1 接口概述 302
8.2 通过接口实现多态性 303
8.3 接口实现 307
8.3.1 显式成员实现 308
8.3.2 隐式成员实现 309
8.3.3 比较显式与隐式接口实现 310
8.4 在实现类和接口之间转换 311
8.5 接口继承 311
8.6 多接口继承 313
8.7 接口上的扩展方法 314
8.8 版本控制 316
8.8.1 C# 8.0之前的接口版本控制 317
8.8.2 C# 8.0之后的接口版本控制 318
8.8.3 受保护接口成员提供了额外的封装和多态性 325
8.9 比较扩展方法和默认接口成员 329
8.10 比较接口和抽象类 330
8.11 比较接口和特性 331
8.12 小结 331
第9章 结构和记录 333
9.1 对比引用相等性和值相等性 336
9.2 结构 337
9.2.1 记录结构 337
9.2.2 记录结构的CIL代码 339
9.3 记录类 341
9.3.1 用属性提供数据存储 344
9.3.2 不可变值类型 345
9.3.3 使用with操作符克隆记录 346
9.3.4 记录构造函数 348
9.3.5 记录结构初始化 348
9.3.6 记录的解构函数 350
9.4 重写object的成员 350
9.4.1 重写ToString() 350
9.4.2 实现值相等性 352
9.4.3 自定义记录的行为 356
9.5 装箱 357
9.6 枚举 363
9.6.1 在枚举和字符串之间转换 367
9.6.2 枚举作为标志使用 368
9.7 小结 372
第10章 良好形式的类型 374
10.1 操作符重载 374
10.1.1 比较操作符 375
10.1.2 二元操作符 376
10.1.3 二元操作符复合赋值 377
10.1.4 条件逻辑操作符 378
10.1.5 一元操作符 378
10.1.6 转换操作符 379
10.1.7 转换操作符设计规范 380
10.2 引用其他程序集 381
10.2.1 引用库 381
10.2.2 用Dotnet CLI引用项目或库 382
10.2.3 在Visual Studio 2022中引用项目或库 382
10.2.4 NuGet打包 382
10.2.5 用Dotnet CLI引用NuGet包 383
10.2.6 在Visual Studio 2022中引用NuGet包 383
10.2.7 调用NuGet包 384
10.3 类型封装 385
10.3.1 类型声明中的访问修饰符public或internal 385
10.3.2 类型成员修饰符protected internal 386
10.3.3 file类型修饰符 386
10.4 定义命名空间 387
10.5 XML注释 390
10.5.1 将XML注释和编程构造关联 390
10.5.2 生成XML文档文件 392
10.6 垃圾回收和弱引用 393
10.7 资源清理 396
10.7.1 终结器 396
10.7.2 使用using语句进行确定性终结 399
10.7.3 垃圾回收、终结和IDisposable 402
10.8 推迟初始化 408
10.9 小结 409
第11章 异常处理 411
11.1 多异常类型 411
11.2 捕捉异常 414
11.3 重新抛出异常 415
11.4 常规catch块 417
11.5 异常处理设计规范 417
11.6 自定义异常 420
11.7 重新抛出包装的异常 423
11.8 小结 425
第12章 泛型 426
12.1 如果C#语言没有泛型 426
12.2 泛型类型概述 431
12.2.1 使用泛型类 431
12.2.2 定义一个简单的泛型类 433
12.2.3 泛型的优点 433
12.2.4 类型参数命名规范 434
12.2.5 泛型接口和结构 434
12.2.6 定义构造函数和终结器 436
12.2.7 指定默认值 437
12.2.8 多个类型参数 438
12.2.9 嵌套泛型类型 441
12.3 约束 441
12.3.1 接口约束 444
12.3.2 类型参数约束 445
12.3.3 unmanaged约束 447
12.3.4 notnull约束 447
12.3.5 struct/class约束 447
12.3.6 多个约束 448
12.3.7 构造函数约束 449
12.3.8 约束继承 451
12.4 泛型方法 454
12.4.1 泛型方法类型推断 455
12.4.2 指定约束 456
12.5 协变性和逆变性 457
12.5.1 使用out类型参数修饰符允许协变性 458
12.5.2 使用in类型参数修饰符允许逆变性 459
12.5.3 数组对不安全协变性的支持 462
12.6 泛型的内部机制 462
12.7 小结 465
第13章 委托和Lambda表达式 466
13.1 委托概述 467
13.1.1 背景 467
13.1.2 委托数据类型 469
13.2 声明委托类型 470
13.2.1 常规用途的委托类型:System.Func和System.Action 470
13.2.2 实例化委托 472
13.3 Lambda表达式 476
13.4 表达式Lambda 479
13.5 匿名方法 481
13.6 委托没有结构相等性 482
13.7 外部变量 484
13.8 表达式树 489
13.8.1 Lambda表达式作为数据使用 489
13.8.2 表达式树作为对象图使用 490
13.8.3 对比委托和表达式树 492
13.8.4 解析表达式树 492
13.9 小结 494
第14章 事件 496
14.1 使用多播委托来编码发布-订阅模式 497
14.1.1 定义订阅者方法 497
14.1.2 定义发布者 498
14.1.3 连接发布者和订阅者 498
14.1.4 调用委托 499
14.1.5 检查空值 500
14.1.6 委托操作符 501
14.1.7 顺序调用 503
14.1.8 错误处理 504
14.1.9 方法返回值和传引用 507
14.2 理解事件 507
14.2.1 事件的作用 507
14.2.2 声明事件 509
14.2.3 编码规范 510
14.2.4 实现自定义事件 514
14.3 小结 515
第15章 支持标准查询操作符的集合接口 516
15.1 集合初始化器 517
15.2 IEnumerable使类成为集合 519
15.3.1 将foreach用于数组 519
15.3.2 将foreach用于IEnumerable 519
15.3.3 foreach循环期间不要修改集合 523
15.3 标准查询操作符 523
15.3.1 使用Where()来筛选 525
15.3.2 使用Select()来投射 526
15.3.3 使用Count()对元素进行计数 528
15.3.4 推迟执行 529
15.3.5 使用OrderBy()和ThenBy()来排序 532
15.3.6 使用Join()执行内连接 535
15.3.7 使用GroupBy分组结果 538
15.3.8 使用GroupJoin()实现一对多关系 539
15.3.9 调用SelectMany() 542
15.3.10 更多标准查询操作符 543
15.4 匿名类型与LINQ 546
15.3.1 匿名类型 546
15.3.2 用LINQ投射成匿名类型 548
15.3.3 匿名类型和隐式局部变量的更多注意事项 549
15.5 小结 552
第16章 使用查询表达式的LINQ 553
16.1 查询表达式概述 553
16.1.1 投射 555
16.1.2 筛选 561
16.1.3 排序 562
16.1.4 let子句 562
16.1.5 分组 563
16.1.6 使用into实现查询延续 566
16.1.7 用多个from子句“扁平化”序列的序列 567
16.2 查询表达式只是方法调用 568
16.3 小结 569
第17章 构建自定义集合 570
17.1 更多集合接口 571
17.1.1 IList和IDictionary 572
17.1.2 ICollection 572
17.2 主要集合类 572
17.2.1 列表集合:List 573
17.2.1 全序 576
17.2.3 查找List 576
17.2.4 字典集合Dictionary 578
17.2.5 已排序集合:SortedDictionary和SortedList 583
17.2.6 栈集合:Stack 584
17.2.7 队列集合:Queue 585
17.2.8 链表:LinkedList 585
17.2.9 Span和ReadOnlySpan 586
17.3 提供索引器 587
17.4 返回null或者空集合 590
17.5 迭代器 591
17.5.1 定义迭代器 591
17.5.2 迭代器语法 592
17.5.3 从迭代器生成值 592
17.5.4 迭代器和状态 594
17.5.5 更多的迭代器例子 595
17.5.6 将yield return语句放到循环中 596
17.5.7 取消更多的迭代:yield break 599
17.5.8 在类中创建多个迭代器 601
17.5.9 yield语句的要求 601
17.6 小结 602
第18章 反射、特性和动态编程 603
18.1 反射 603
18.1.1 使用System.Type访问元数据 604
18.1.2 成员调用 606
18.1.3 泛型类型上的反射 610
18.2 nameof操作符 612
18.3 特性 613
18.3.1 自定义特性 617
18.3.2 查找特性 617
18.3.3 使用构造函数初始化特性 618
18.3.4 System.AttributeUsageAttribute 622
18.3.5 具名参数 623
18.3.6 预定义特性 625
18.3.7 System.ConditionalAttribute 625
18.3.8 System.ObsoleteAttribute 627
18.3.9 泛型特性 628
18.3.10 Caller*特性 628
18.4 使用动态对象进行编程 631
18.4.1 使用dynamic调用反射 632
18.4.2 dynamic的原则和行为 633
18.4.3 为什么需要动态绑定 635
18.4.4 比较静态编译和动态编程 635
18.4.5 实现自定义动态对象 636
18.5 小结 639
第19章 多线程处理 640
19.1 多线程处理基础 642
19.2 异步任务 645
19.2.1 为什么要用TPL 646
19.2.2 理解异步任务 647
19.2.3 任务延续 650
19.2.4 用AggregateException处理任务的未处理异常 655
19.3 取消任务 660
19.3.1 Task.Run()是Task.Factory.StartNew()的简化形式 663
19.3.2 长时间运行的任务 664
19.3.3 对任务进行资源清理 665
19.4 使用System.Threading 665
19.5 小结 666
第20章 基于任务的异步模式编程 667
20.1 以同步方式调用高延迟操作 668
20.2 使用TPL异步调用高延迟操作 670
20.3 使用async/await实现基于任务的异步模式 673
20.4 异步返回ValueTask 678
20.5 异步流 680
20.6 IAsyncDisposable接口以及await using声明和语句 684
20.7 通过IAsyncEnumerable来使用LINQ 684
20.8 从异步方法返回void 686
20.9 异步Lambda和本地函数 689
20.10 任务调度器和同步上下文 694
20.11 async/await和Windows UI 695
20.12 小结 698
第21章 并行迭代 699
21.1 并行迭代 699
20.2 并行执行LINQ查询 706
21.3 小结 711
第22章 线程同步 712
22.1 线程同步的意义 713
22.1.1 用Monitor同步 716
22.1.2 使用lock关键字 718
22.1.3 lock对象的选择 720
22.1.4 为什么要避免锁定this、typeof(type)和string 721
22.1.5 避免用MethodImplAttribute同步 721
22.1.6 将字段声明为volatile 722
22.1.7 使用System.Threading.Interlocked类 722
22.1.8 多个线程时的事件通知 724
22.1.9 同步设计最佳实践 725
22.1.10 更多同步类型 726
22.1.11 线程本地存储 733
22.2 计时器 736
22.3 小结 737
第23章 平台互操作性和不安全代码 739
23.1 平台调用 740
23.1.1 声明外部函数 740
23.1.2 参数的数据类型 740
23.1.3 本机大小的整数 742
23.1.4 使用ref而不是指针 742
23.1.5 为顺序布局使用StructLayoutAttribute 743
23.1.6 内联数组 743
23.1.7 跳过局部变量的初始化 744
23.1.8 错误处理 744
23.1.9 使用SafeHandle 746
23.1.10 调用外部函数 747
23.1.11 用包装器简化API调用 749
23.1.12 函数指针映射到委托 750
23.1.13 设计规范 750
23.2 指针和地址 751
23.2.1 不安全代码 751
23.2.2 指针声明 752
23.2.3 指针赋值 754
23.2.4 指针解引用 756
23.2.5 访问被引用物类型的成员 758
23.3 通过委托执行不安全代码 758
23.4 小结 760
第24章 公共语言基础结构(CLI) 761
24.1 CLI的定义 762
24.2 CLI的实现 762
24.2.1 Microsoft .NET Framework 764
24.2.2 .NET Core 764
24.2.3 Xamarin 765
24.3 .NET Standard 765
24.4 BCL 765
24.5 C#编译成机器码 765
24.6 运行时 767
24.6.1 垃圾回收 767
24.6.2 平台可移植性 769
24.6.3 性能 769
24.7 程序集、清单和模块 770
24.8 公共中间语言 771
24.9 公共类型系统 772
24.10 公共语言规范 772
24.11 元数据 773
24.12 NET Native和AOT编译 773
24.13 小结 774
在软件工程的发展进程中,计算机编程范式经历了几次重大的思维模式的转变。每个思维模式都以前一个为基础,宗旨都是增强代码的组织和降低复杂性。本书旨在带领大家体验相同的思维模式转变过程。
本书前面几章介绍顺序编程结构。在这样的编程结构中,语句按执行顺序来写。该结构的问题在于,随着需求的增加,复杂性也呈指数级增加。因此,为了降低复杂性,代码块被移到方法中,形成结构化编程模型。在这样的模型中,可以从一个程序中的多个位置调用同一个代码块,不需要进行复制。但即使有这种结构,程序还是很快就会变得臃肿不堪,因而需要进一步抽象。在此基础上,又提出面向对象编程的概念,这将在第6章开始讨论。在此之后,大家将继续学习其他编程方法,比如基于接口的编程和LINQ(以及它促使集合API发生的改变),并最终学习通过特性(attribute)进行初级的声明性编程(第18章)。
本书有以下三个主要职能:
全面讲述C#语言,其内容远远超过那些简单的普通教程,旨在为大家进行高效软件开发打下坚实基础;
对于已经熟悉C#语言的读者,本书探讨一些较为复杂的编程思想,并深入讨论语言最新版本(C# 12.0和.NET 8)的新功能;
它是大家“如影随形的良师益友”,即便是已经精通这门语言的读者。
掌握C#语言的关键在于,尽快着手进行编程。不要等自己成为一名理论专家之后才开始动手写代码。所以,不要犹豫,马上动手。作为迭代开发思想的拥趸,我写这本书的愿望是,即使刚开始学习编程的新手,也能在第2章结束时动手写基本的C#代码。
许多主题没有在本书中讨论。本书不讨论ASP.NET、Entity Framework、Mauri、智能客户端开发以及分布式编程等主题。虽然这些主题与.NET有关,但它们都值得专门用一本书来讲解。幸好,本书的重点是C#语言及其基类库中的类型。本书将帮助大家完全掌握C#语言基础,让大家游刃有余地深入高级编程领域。
本书面向的读者
写作本书的时候,我面临的挑战是如何让高级开发人员持续保持专注但同时又不至于因为大量使用assembly、link、chain、thread和fusion等术语而使初学者产生挫败感(否则许多人会以为这本书是在讲冶金术而不是程序设计)。本书的主要读者是已经有一定编程经验并想多学一种语言来“傍身”的开发人员。为此,我精心设计了全书的内容,使其足以满足大多数开发人员的需求,让他们有获得感。
初学者:如果是编程新手,本书将帮助你从入门级程序员过渡成为C#专业开发人员,让大家未来不至于害怕面对任何C#编程任务。本书不但要教语法,还会教大家养成良好的编程习惯,帮助你为将来的编程生涯奠定良好的基础。
熟悉结构化编程的程序员:学习外语,最好的方法是“沉浸法”。与此类似,学习计算机语言的话,最好的方法是边动手边学习,而不是一直“纸上谈兵”。基于这个前提,本书最开始介绍的内容对熟悉结构化编程的开发人员而言,是很容易上手的。到快要学完第5章时,就可以开始写基本的控制流程序了。然而,要成为真正的C#开发人员,记住语法只是第一步。为了从简单程序过渡到企业级开发,C#开发人员还必须完全掌握从对象及其关系并从这个角度来思考问题。为此,第6章开始介绍类和面向对象开发。C语言、COBOL语言和FORTRAN等结构化编程语言虽然仍然在发挥作用,但作用会越来越小,所以软件工程师应该逐渐了解面向对象开发。C#语言是实现这一思维模式转变的理想语言,因为它本来就是基于“面向对象开发”这一中心思想来设计的。
熟悉“基于对象”和“面向对象”理念的开发人员:C 、Java、Python、TypeScript、Visual Basic和Java程序员都可归于此类。对于分号和大括号,他们可一点儿都不陌生!简单浏览一下第1章的代码,就会发现,就其核心而言,C#语言是类似于大家早就熟悉的C和C 风格的语言。
C#专家:对于精通C#的人,本书有很多不太常见的语法供大家参考。此
外,针对其他地方较少强调的一些语言细节以及微妙之处,我提出了自己的见解。最重要的是,本书提供了编写可靠和易维护代码的指导原则及模式。
本书也适合用作C#教材。从C# 3.0到C# 12.0,最重要的一些增强如下:
字符串插值(第2章);
隐式类型的变量(第3章);
元组(第3章);
可空引用类型(第3章);
模式匹配(第4章);
扩展方法(第6章);
分部方法(第6章);
默认接口成员(第8章);
匿名类型(第12章);
泛型(第12章);
Lambda语句和表达式(第13章);
表达式树(第13章);
标准查找操作符(第15章);
查询表达式(第16章);
动态编程(第18章);
用任务编程库(TPL)和async进行多线程编程(第20章);
用PLINQ进行并行查询处理(第21章);
并发集合(第22章)。
考虑到许多人还不熟悉这些主题,所以本书将对它们展开详细的讨论。涉及C#高级开发主题的还有指针,具体参见第23章。即使是有经验的C#开发人员,也未必能够透彻地理解这一主题。
本书特色
本书作为权威的编程语言参考指南,遵循的是核心C#语言规范。为了帮助读者理解C#语言的构造,书中用大量的例子来演示每个特性,并为每个概念提供相应的指导原则和最佳实践,以确保代码能够顺利编译、避免留下隐患并获得最佳的可维护性。
为了增强代码的可读性,书中所有代码都采用了特殊的格式,每章开篇还采用了思维导图的方式来高度概括当前这一章的内容。
网站
本书有配套的交互式网站,提供在线阅读内容(英文版)并支持全文搜索。该网站后续还要上线更多的功能,比如对许多代码清单进行交互式代码编辑和客户端编译的功能,使你能够专注于语言本身,不会因为安装或dotnet设置问题而分心。
源代码下载
除了网站,所有英文版和中文版源代码都可以从GitHub获取,网址如下:
访问有困难,也可以直接从译者主页获取。
通过以上方式,你可以下载或在本地复制,随时查看代码或者进行修改,并实际体验效果。老话说得好,纸上得来终觉浅,绝知此事要躬行。动手实际操练,始终都是学习编程语言细节的有效方式。
C#设计规范
本书新版本最重大的改进是增加了大量设计规范,下面的例子来自第18章。
设计规范
DO name custom attribute classes with the suffix Attribute.
要在为自定义特性类命名时添加Attribute后缀。
熟悉语法的程序员和能够巧妙写出高效代码的专家,两者的分水岭就是这样的设计规范。专家不仅能让代码通过编译,同时还遵循最佳实践,降低出现bug的概率,并使代码的维护变得更容易。设计规范强调了软件开发期间必须注意一些关键原则。
示例代码
虽然本书大多数代码都能在公共语言基础结构(Common Language Infrastructure,CLI)的任何实现上运行,但重点仍然是.NET实现。本书也较少使用特定于某些平台或厂商的库,除非需要解释仅与那些平台相关的重要概念(例如,解释如何正确处理Windows单线程UI)。
我们以代码清单1.21为例:
代码清单1.21 为代码添加注释
public class CommentSamples
{
public static void Main()
{
string firstName; // 用于存储名字的变量
string lastName; // 用于存储姓氏的变量
Console.WriteLine(“嘿,你!”);
Console.Write /* 不换行 */ (“请输入你的名字: “);
firstName = Console.ReadLine();
Console.Write /* 不换行 */ (“请输入你的姓氏: “);
lastName = Console.ReadLine();
/* 使用字符串插值在控制台上显示问候语*/
Console.WriteLine($”你的全名是{ firstName } { lastName }。”);
// 这是程序清单
// 的结尾
}
}
具体的格式解释如下:
配套资源中所有源代码都进行了彩色标注(正文黑白印刷的纸质版图书会呈现不同的灰度);
省略号表示无关代码已省略:
// …
某些代码行添加的底纹,表明是对上个代码清单的修改,或者强调当前正在讲解的某个主题,如下面的代码清单2.23所示:
代码清单2.23 错误;string是不可变的
Console.Write(“输入文本: “);
string text = Console.ReadLine();
// UNEXPECTED: text并没有转换为全大写
text.ToUpper();
Console.WriteLine(text);
某些代码清单后列出了相应的控制台输出。由用户输入的内容加粗显示,如以下输出1.7所示:
输出1.7
嘿,你!
请输入你的名字: Inigo
请输入你的姓氏: Montoya
你的全名是Inigo Montoya。
虽然我也可以在书中提供完整的代码以便读者复制,但这样会让大家分心。因此,请自行在程序中修改示例代码。
还可以从译者主页获取中文版示例代码和更多配套资源。
思维导图
每章开篇的思维导图旨在方便读者快速了解当前章节的内容。下面是一个例子(摘自第6章)。
在思维导图的中心,显示的是当前章的主题,所有高级主题都围绕该中心展开。利用思维导图,可方便地搭建自己的知识体系,可以从一个主题出发,更清楚地理解其周边的各个具体概念,避免中途纠缠于一些旁枝末节的小问题。
编程水平分级阅读说明
根据个人编程水平的不同,可以借助于书中提供的以下标志来轻松找到适合自己的内容:
初学者主题,特别为刚入门的程序员提供的定义或解释;
高级主题,使有经验的开发人员将注意力放在他们最关心的内容上;
注意,强调读者应注意的要点;
语言对比,分散于正文中的“语言对比”描述C#和其他语言的关键差异,为具有其他语言背景的读者提供C#参考。
本书内容的组织
总的来说,软件工程的宗旨是管理复杂性。本书基于该宗旨来组织内容。第1章~第5章介绍结构化编程,学完这些内容后,可以立即动手写一些功能简单的代码。第6章~第10章介绍C#的面向对象构造,新手应在完全理解这几章的内容后,再开始接触本书其余部分更高级的主题。第12章~第14章介绍更多用于降低复杂性的构造,讲解了当今几乎所有程序都要用到的通用设计模式。理解它们之后,才能更轻松地理解如何通过反射和特性来进行动态编程。后续章节将广泛运用它们来实现线程处理和互操作性。
本书最后专门用一章(第24章)讲解CLI。这一章在开发平台的背景下讲述C#语言。之所以放到最后,是因为它并非C#特有,而且不涉及语法和编程风格问题。不过,本章适合在任何时候阅读,或许最恰当的时机是在完成第1章的阅读之后。
每章的内容简介如下。
第1章——C#概述:本章在展示了用C#语言写的Hello World程序之后对其进行细致分析。目的是让读者熟悉C#程序的“外观和感觉”,并理解如何编译和调试自己的程序。另外,还简单描述了C#程序的执行上下文及其中间语言(Intermediate Language,IL)。
第2章——数据类型:任何有用的程序都要处理数据,本章介绍C#语言的基元数据类型。
第3章——深入数据类型:本章深入讲解数据类型的两大类别:值类型和引用类型。然后讲解隐式类型的局部变量、元组、可空修饰符以及C# 8.0引入的可空引用类型。最后深入讨论基元数组结构。
第4章——操作符和控制流:计算机最擅长重复性操作,为了利用该能力,需要知道如何在程序中添加循环和条件逻辑。本章还讨论C#操作符、数据转换和预处理器指令。
第5章——方法和参数:本章详细讨论方法及其参数,其中包括传值、传引用和通过out参数返回数据。从C# 4.0开始支持默认参数,本章解释了具体如何使用。
第6章——类:前面几章介绍类的基本构成元素,本章将把这些构造合并为具有完整功能的类型。类是面向对象技术的核心,它定义了对象模板。
第7章——继承:继承是许多开发人员的基本编程手段,C#语言更是提供了一些独特构造,比如new修饰符。本章讨论继承语法的细节,其中包括重写(overriding)。
第8章——接口:本章讨论如何利用接口来定义类之间的“可进行版本控制的交互契约”(versionable interaction contract)。C#语言同时包含显式和隐式接口成员实现,可以实现一个额外的封装等级,这是其他大多数语言所不支持的。本章最后用一节讨论接口版本控制问题,并强调C# 8.0的引入的“默认接口成员”的作用。
第9章——结构和记录:C# 9.0为结构引入了记录(record)的概念,并在C# 10中把它扩展到值类型(即record struct)。尽管定义引用类型的情况更普遍,但有时确实需要定义行为与C#语言内置的基元类型相似的值类型。本章介绍如何创建自定义结构(struct),并强调它的一些要特别注意的地方。
第10章——良好形式的类型:本章讨论更高级的类型定义,解释如何实现操作符,比如 和转型操作符,并描述如何将多个类封装到一个库中。另外,还演示了如何定义命名空间和XML注释,并讨论如何基于垃圾回收机制来设计令人满意的类。
第11章——异常处理:本章延伸讨论第5章引入的异常处理机制,描述如何利用异常层次结构创建自定义异常。另外,还强调异常处理的一些最佳实践。
第12章——泛型:泛型或许是C# 1.0最缺少的功能。本章全面讨论自C# 2.0引入的泛型机制。此外,C# 4.0增加了对协变和逆变的支持,本章要在泛型背景中探讨它们。
第13章——委托和Lambda表达式:正是因为委托,才使C#与其前身语言(C和C 等)有了显著不同,它定义了在代码中处理事件的模式。这几乎完全消除了写轮询例程的必要。Lambda表达式是使C# 3.0的LINQ成为可能的关键概念。通过本章的学习,将知道Lambda表达式是在委托的基础上构建起来的,它提供了比委托更优雅和简洁的语法。本章内容是第14章的基础。
第14章——事件:封装起来的委托(称为事件)是公共语言运行时(Common Language Runtime,CLR)的核心构造。本章还要讲解自C# 2.0引入的另一个特性,即匿名方法。
第15章——支持标准查询操作符的集合接口:通过讨论新的Enumerable类的扩展方法,介绍C# 3.0引入的一些简单而强大的改变。Enumerable类造就了全新的集合API,即“标准查询操作符”,本章对其进行了详细讨论。
第16章——使用查询表达式的LINQ:如果只使用标准查询操作符,会形成让人难以辨认的长语句。但查询表达式提供了一种类似SQL风格的语法,有效地解决了该问题。本章会详细讨论这种表达式。
第17章——构建自定义集合:构建用于操纵业务对象的自定义API时,经常都需要创建自定义集合。本章讨论具体如何做。还要介绍能使自定义集合的构建变得更简单的上下文关键字。
第18章——反射、特性和动态编程:20世纪80年代末,程序结构的思维模式发生了根本性的变化,面向对象的编程是这个变化的基础。类似地,特性(attribute)使声明性编程和嵌入元数据成为可能,因而引入了一种新的思维模式。本章探讨特性的方方面面,并讨论如何通过反射机制来获取它们。本章还讨论如何通过基类库(Base Class Library,BCL)中的序列化框架来进行文件的输入输出。C# 4.0新增了dynamic关键字,能将所有类型检查都移至运行时进行,因而极大扩展了C#语言的能力。
第19章——多线程处理:大多数现代程序都要求用线程执行长时间运行的任
务,同时确保对并发事件的快速响应。随着程序变得越来越复杂,必须采取其他措施来保护这些高级环境中的数据。多线程应用程序的编写比较复杂。本章讨论如何操纵线程(包括如何取消)以及如何在任务上下文中进行异常处理。
第20章——基于任务的异步模式编程:本章深入探讨基于任务的异步模式以及相应的async/await语法。它极大地简化了多线程编程。另外,本章还要讲解C# 8.0引入的异步流的概念。
第21章——并行迭代:为了优先性能,一个简单的方式是使用Parallel对象或并行LINQ(PLINQ)库在数据上进行并行迭代。
第22章——线程同步:本章对前几章的内容进行扩展,演示如何利用一些内建线程处理模式来简化对多线程代码的显式控制。
第23章——平台互操作性和不安全的代码:必须意识到,C#语言仍然是相对年轻的一种语言,现有的许多代码都是用其他语言写成的。为了用好这些现有代码,C#语言通过P/Invoke提供了对互操作性(调用非托管代码)的支持。此外,C#语言允许使用指针,也允许执行直接内存操作。虽然使用了指针的代码要求特殊权限才能运行,但它具有与C风格的API完全兼容的能力。
第24章——公共语言基础结构(CLI):事实上,C#被设计成一种在CLI的顶部工作的最有效的编程语言。本章讨论C#程序与底层“运行时”及其规范的关系。
希望本书成为你深度掌握C#编程技能的理想参考用书。另外,在能熟练使用C#语言后,本书仍然可以作为案头参考,供你遇到不熟悉的领域时参考。
——马克•米凯利斯( Mark Michaelis)
评论
还没有评论。