本书详细介绍了C#的特点、新增功能以及它与C和C++的异同,是一本真正揭示C#技术内幕的书籍。\r\n 本书既适合C#的初学者阅读,也适合已经使用过C#语言进行编程并希望掌握高级功能的开发人员参考。
译者序\r\n序一\r\n序二\r\n前言\r\n\r\n第一部分 C#类的基本原理\r\n\r\n第1章 建立C#应用程序和库\r\n\r\n1.1 “Hello,World”--命令行版本\r\n1.2 “Hello,World”的代码解释\r\n1.2.1 一站式编程\r\n1.2.2 名称空间\r\n1.2.3 类和成员\r\n1.2.4 Main方法\r\n1.2.5 System.Console.WriteLine方法\r\n1.2.6 名称空间和using指令\r\n1.2.7 框架代码\r\n1.2.8 类的二义性\r\n1.3 “Hello,World”--Visual Studio.NET版本\r\n1.4 编译和运行.NET应用程序\r\n1.5 “Hello,World”内部\r\n1.6 配件和模块\r\n1.6.1 配件概述\r\n1.6.2 配件的好处\r\n1.6.3 构建配件\r\n1.6.4 创建共享配件\r\n1.6.5 使用全局配件缓存\r\n1.7 小结\r\n\r\n第2章 .NET类型系统\r\n\r\n2.1 所有东西都是对象\r\n2.2 值类型和引用类型\r\n2.2.1 值类型\r\n2.2.2 引用类型\r\n2.3 装箱和拆箱\r\n2.3.1 将值类型转换为引用类型\r\n2.3.2 将引用类型转换为值类型\r\n2.3.3 更多的装箱例子\r\n2.4 类型和别名\r\n2.5 类型之间的转换\r\n2.6 CTS的好处\r\n2.6.1 语言互操作性\r\n2.6.2 单根的对象层次结构\r\n2.6.3 类型安全\r\n2.7 小结\r\n\r\n第3章 类和结构\r\n\r\n3.1 定义类\r\n3.2 类成员\r\n3.3 访问修饰符\r\n3.4 Main方法\r\n3.4.1 命令行参数\r\n3.4.2 从Main方法返回值\r\n3.4.3 多个Main方法\r\n3.5 构造器\r\n3.5.1 静态成员和实例成员\r\n3.5.2 构造器初始化器\r\n3.5.3 在构造器初始化器中指定运行时信息\r\n3.6 常量与只读字段\r\n3.6.1 常量\r\n3.6.2 只读字段\r\n3.7 继承\r\n3.7.1 多个接口\r\n3.7.2 封闭的类\r\n3.8 在C#中定义结构\r\n3.8.1 结构的使用\r\n3.8.2 使用结构的原则\r\n3.9 小结\r\n\r\n第4章 方法\r\n\r\n4.1 值和引用参数\r\n4.1.1 ref方法参数\r\n4.1.2 ont方法参数\r\n4.2 再论值和引用参数\r\n4.3 方法重载\r\n4.3.1 重载构造器\r\n4.3.2 继承与重载\r\n4.4 可变的方法参数\r\n4.5 虚拟方法\r\n4.5.1 方法重定义\r\n4.5.2 多态性\r\n4.5.3 new和虚拟方法\r\n4.5.4 从构造器调用虚拟方法\r\n4.6 静态方法\r\n4.6.1 访问类成员\r\n4.6.2 静态构造器\r\n4.7 小结\r\n\r\n第5章 特性、数组和索引器\r\n\r\n5.1 特性是灵巧字段\r\n5.1.1 定义和使用特性\r\n5.1.2 特性的内幕\r\n5.1.3 继承特性\r\n5.1.4 特性的高级用途\r\n5.2 数组\r\n5.2.1 声明数组\r\n5.2.2 一维数组示例\r\n5.2.3 多维数组\r\n5.2.4 查询秩\r\n5.2.5 锯齿形数组\r\n5.3 使用索引器像对待数组那样对待对象\r\n5.3.1 定义索引器\r\n5.3.2 索引器例子\r\n5.3.3 索引器的内幕\r\n5.3.4 设计原则\r\n5.4 小结\r\n\r\n第6章 属性\r\n\r\n6.1 属性简介\r\n6.2 定义属性\r\n6.3 查询属性\r\n6.3.1 类属性\r\n6.3.2 方法属性\r\n6.3.3 字段属性\r\n6.4 属性参数\r\n6.4.1 位置参数和命名参数\r\n6.4.2 使用命名过的参数时的常见错误\r\n6.4.3 有效的属性参数类型\r\n6.5 AttributeUsage属性\r\n6.5.1 定义属性目标\r\n6.5.2 单次使用和多次使用的属性\r\n6.5.3 指定属性继承规则\r\n6.6 属性标识符\r\n6.7 预定义的属性\r\n6.7.1 Conditional属性\r\n6.7.2 Obsolete属性\r\n6.7.3 CLSCompliant属性\r\n6.7.4 DllImport和StructLayout属性\r\n6.7.5 配件属性\r\n6.8 上下文属性\r\n6.9 小结\r\n\r\n第7章 接口\r\n\r\n7.1 接口的用途\r\n7.2 声明接口\r\n7.3 实现接口\r\n7.3.1 使用is查询实现\r\n7.3.2 使用as查询实现\r\n7.3.3 接口与替代技术\r\n7.4 显式地限定接口成员名\r\n7.4.1 用接口进行名称隐藏\r\n7.4.2 避免名称的二义性\r\n7.5 接口与继承\r\n7.6 组合接口\r\n7.7 小结\r\n\r\n第二部分 编写代码\r\n\r\n第8章 表达式和操作符\r\n\r\n8.1 操作符的定义\r\n8.2 操作符的优先级\r\n8.2.1 C#如何决定优先级\r\n8.2.2 左结合性和右结合性\r\n8.2.3 实际使用\r\n8.3 C#操作符\r\n8.3.1 基本表达式操作符\r\n8.3.2 算术操作符\r\n8.4 数值转换\r\n8.5 位操作符\r\n8.6 关系操作符\r\n8.7 简单赋值操作符\r\n8.8 条件操作符\r\n8.9 小结\r\n\r\n第9章 程序流控制\r\n\r\n9.1 选择语句\r\n9.1.1 if语句\r\n9.1.2 switch语句\r\n9.2 迭代语句\r\n9.2.1 while语句\r\n9.2.2 do/while语句\r\n9.2.3 for语句\r\n9.2.4 foreach语句\r\n9.3 使用跳转语句进行分支\r\n9.3.1 break语句\r\n9.3.2 continue语句\r\n9.3.3 声名狼藉的goto语句\r\n9.3.4 return语句\r\n9.4 小结\r\n\r\n第10章 字符串处理和正则表达式\r\n\r\n10.1 字符串\r\n10.1.1 字符串的格式化\r\n10.1.2 格式指定符\r\n10.1.3 对象和ToString\r\n10.1.4 数字字符串分析\r\n10.1.5 字符串和DateTime\r\n10.1.6 字符串的编码方式\r\n10.1.7 StringBuilder类\r\n10.1.8 分解字符串\r\n10.1.9 扩展字符串\r\n10.1.10 字符串扣留\r\n10.2 正则表达式\r\n10.2.1 Match和MatchCollection\r\n10.2.2 组和捕获\r\n10.2.3 字符串-修改表达式\r\n10.2.4 正则表达式选项\r\n10.2.5 编译正则表达式\r\n10.3 小结\r\n\r\n第11章 用流进行文件I/O\r\n\r\n11.1 流类\r\n11.1.1 FileStream\r\n11.1.2 StreamReader和StreamWriter\r\n11.1.3 内存和缓冲流\r\n11.1.4 字符串读取器和写出器\r\n11.1.5 二进制读取器和写出器\r\n11.2 文件系统类\r\n11.2.1 Directory和DirectoryInfo\r\n11.2.2 File和FileInfo\r\n11.2.3 分析路径\r\n11.3 流的非控制台使用方式\r\n11.3.1 Windows OpenFileDialog\r\n11.3.2 读取Web页面\r\n11.4 串行化\r\n11.4.1 使用BinaryFormatter进行串行化\r\n11.4.2 使用SoapFormatter\r\n11.4.3 使用XmlSerializer进行串行化\r\n11.4.4 实现ISerializable\r\n11.5 小结\r\n\r\n第12章 用异常进行错误处理\r\n\r\n12.1 异常处理概述\r\n12.2 基本的异常处理语法\r\n12.2.1 抛出异常\r\n12.2.2 捕获异常\r\n12.2.3 重新抛出一个异常\r\n12.2.4 使用finally进行清理\r\n12.3 重试代码\r\n12.4 错误处理技术的比较\r\n12.4.1 异常处理相对于返回编码的好处\r\n12.4.2 在正确的上下文中处理错误\r\n12.4.3 提高代码的可读性\r\n12.4.4 从构造器抛出异常\r\n12.5 使用System.Exception类\r\n12.5.1 构造一个Exception对象\r\n12.5.2 使用StackTrace特性\r\n12.5.3 捕获多个异常类型\r\n12.5.4 派生自己的Exception类\r\n12.6 用异常处理设计代码\r\n12.6.1 try块的设计问题\r\n12.6.2 catch块的设计问题\r\n12.7 小结\r\n\r\n第13章 操作符重载和用户定义的转换\r\n\r\n13.1 操作符重载\r\n13.1.1 操作符重载的语法\r\n13.1.2 操作符重载的规则和限制\r\n13.1.3 操作符重载示例\r\n13.1.4 RGB颜色递增示例\r\n13.1.5 操作符重载的设计方针\r\n13.2 用户定义的转换\r\n13.2.1 用户定义的转换语法\r\n13.2.2 用户定义的转换的规则和限制\r\n13.2.3 用户定义的转换示例\r\n13.3 小结\r\n\r\n第14章 委托和事件处理器\r\n\r\n14.1 将委托作为回调方法使用\r\n14.2 委托的内幕\r\n14.3 将委托定义为静态成员\r\n14.4 只在需要时创建委托\r\n14.5 多点委托\r\n14.6 使用多点委托定义事件\r\n14.7 小结\r\n\r\n第15章 使用XML进行文档记录\r\n\r\n15.1 入门\r\n15.1.1 添加元素\r\n15.1.2 可以接受注释的代码构造\r\n15.2 编译器产生的元素ID\r\n15.2.1 字段、特性、事件和索引器\r\n15.2.2 方法\r\n15.3 良构的XML\r\n15.4 注释网页\r\n15.5 元素标记和属性\r\n15.5.1 标记和cref属性\r\n15.5.2 和标记\r\n15.5.3 标记\r\n15.5.4 标记\r\n15.6 定制的格式化\r\n15.7 XML与数据\r\n15.8 小结\r\n\r\n第三部分 高级C#\r\n\r\n第16章 数值处理和Math类\r\n\r\n16.1 C#和.NET中的数值支持\r\n16.1.1 Decimal类型是原始的类型吗\r\n16.1.2 数值后缀\r\n16.1.3 数值类型的各种名称\r\n16.1.4 数值字面值\r\n16.2 整数范围和溢出规则\r\n16.3 Decimal类型\r\n16.4 System.Math类\r\n16.4.1 System.Math常量\r\n16.4.2 处理数值的正负号\r\n16.4.3 最小值和最大值\r\n16.4.4 用于取整和截短的方法\r\n16.5 小结\r\n\r\n第17章 集合和对象枚举\r\n\r\n17.1 实现枚举接口\r\n17.1.1 使用枚举器对象\r\n17.1.2 使用foreach语句处理集合\r\n17.2 为什么需要两个接口\r\n17.3 构造枚举器对象\r\n17.4 创建具有版本的枚举器\r\n17.5 将IEnumerable和IEnumerator合并\r\n17.6 在允许枚举时保护数据\r\n17.7 值类型\r\n17.7.1 性能问题\r\n17.7.2 修改值类型的集合数据\r\n17.8 小结\r\n\r\n第18章 多线程和异步编程\r\n\r\n18.1 线程入门\r\n18.2 线程处理的基本知识\r\n18.2.1 创建线程和Thread对象\r\n18.2.2 管理线程的生存周期\r\n18.2.3 销毁线程\r\n18.2.4 对线程进行调度\r\n18.3 与线程进行数据通信\r\n18.4 线程安全和同步\r\n18.4.1 使用Monitor类保护代码\r\n18.4.2 通过C# lock语句使用监视锁\r\n18.4.3 使用Mutex类对代码进行同步\r\n18.5 使用委托调用异步方法\r\n18.6 线程安全和.NET类\r\n18.7 线程方针\r\n18.7.1 何时应该使用线程\r\n18.7.2 何时不应该使用线程\r\n18.8 小结\r\n\r\n第19章 利用反射查询元数据\r\n\r\n19.1 Reflection API层次结构\r\n19.2 Type类\r\n19.3 获得Type引用\r\n19.3.1 获取一个实例的类型\r\n19.3.2 从名称获取类型\r\n19.3.3 查询类型\r\n19.4 处理配件和模块\r\n19.4.1 遍历配件的类型\r\n19.4.2 列出配件的模块\r\n19.5 使用反射进行晚绑定\r\n19.6 使用反射实现抽象工厂\r\n19.7 动态地生成代码\r\n19.8 小结\r\n\r\n第20章 固定和内存管理\r\n\r\n20.1 垃圾收集\r\n20.2 重定义Finalize\r\n20.3 强行进行垃圾收集\r\n20.4 Dispose模式\r\n20.5 IDisposable接口\r\n20.5.1 派生的可处置类\r\n20.5.2 防止重复处置\r\n20.5.3 对Dispose的语言支持\r\n20.6 垃圾收集器代\r\n20.7 弱引用\r\n20.8 不安全的代码\r\n20.9 固定\r\n20.9.1 固定数组元素\r\n20.9.2 间接引用成员操作符\r\n20.9.3 使用stackalloc\r\n20.10 小结\r\n\r\n第21章 从C#应用程序使用COM\r\n\r\n21.1 COM在当今的.NET环境中处于什么位置\r\n21.2 从C#使用COM组件\r\n21.2.1 创建ATL组件\r\n21.2.2 从.NET应用程序使用COM组件\r\n21.2.3 绑定和使用COM组件\r\n21.2.4 使用COM组件进行动态类型发现\r\n21.2.5 对COM对象进行晚绑定\r\n21.3 事件处理\r\n21.3.1 COM连接点的工作原理\r\n21.3.2 创建一个产生事件的ATL COM组件\r\n21.3.3 使用委托进行事件处理\r\n21.3.4 在.NET应用程序中接收非托管的COM事件\r\n21.4 使用COM集合\r\n21.4.1 使用ATL创建COM集合对象\r\n21.4.2 在.NET应用程序中使用COM集合\r\n21.4.3 遍历.NET集合中的元素\r\n21.5 在托管代码中重用COM组件\r\n21.5.1 包含\r\n21.5.2 聚合\r\n21.5.3 通过混合模式的继承进行重用\r\n21.5.4 通过混合模式的包含进行重用\r\n21.5.5 .NET对COM线程模型和场所的感知\r\n21.6 将方法关键字映射到IDL属性\r\n21.7 小结\r\n\r\n第22章 在非托管代码中使用.NET组件\r\n\r\n22.1 通过COM创建和使用.NET组件\r\n22.1.1 从配件产生类型库和注册配件\r\n22.1.2 从Visual Basic 6客户使用组件\r\n22.1.3 COM interop的内部机理\r\n22.1.4 探究产生的类型库\r\n22.2 使用属性影响产生的类型库元数据\r\n22.2.1 修改接口类型\r\n22.2.2 修改GUID和ProgID\r\n22.2.3 对COM隐藏公共类型\r\n22.2.4 修改类型的编组行为\r\n22.3 异常处理:.NET与COM\r\n22.4 在非托管事件接收器中处理来自.NET组件的事件\r\n22.4.1 创建产生事件的.NET组件\r\n22.4.2 在Visual Basic客户应用程序中处理事件\r\n22.5 .NET组件中的线程从属关系\r\n22.6 小结\r\n\r\n第23章 安全性\r\n\r\n23.1 .NET安全性\r\n23.2 可检验的类型安全\r\n23.3 代码签名\r\n23.3.1 私有配件\r\n23.3.2 强名称配件\r\n23.3.3 全局配件缓存\r\n22.3.4 延迟签名配件\r\n23.4 密码服务\r\n23.5 代码访问安全性\r\n23.5.1 证据\r\n23.5.2 安全策略\r\n23.5.3 配置安全\r\n23.5.4 CASpol\r\n23.5.5 强制式和声明式CAS\r\n23.5.6 标识权限\r\n23.5.7 配件权限请求\r\n23.6 基于角色的安全\r\n23.6.1 PrincipalPermission请求\r\n23.6.2 模仿\r\n23.7 隔离的存储\r\n23.8 小结\r\n\r\n附录A MSIL指令表
为什么要写这本书
20年来,我为各种平台开发软件(从System/38,到AS/400,到OS/2和现在的Microsoft Windows),我可以毫无保留地说我已经完成了使命。向往夜晚和周末的日子成了遥远的记忆;这种向往并不是因为我想回家,而是因为在家可以毫无干扰地整日整夜工作。我感到所有值得做的事都已经完成了。这种感觉在2000年初发生了变化。
2000年,就在Microsoft在佛罗里达州奥兰多发布.NET之前的几个月,我的一位好朋友告诉我一个关于很“酷”的C#语言和运行时环境框架(名为NGWS)的秘密。尽管他谈论这个话题时充满了热情,但是我并不以为然,我回想起了那些失败的总体体系结构(比如IBM的SAA和Microsoft的DNA)。结果,我抱着怀疑的态度开始接触C#和.NET。
但是随着我逐渐了解了这种新语言和平台,我原来的热情逐渐回来了。近两年,我起床时不再带着对重复编写烦人的应用程序的恐惧,而是满心期望学习.NET的令人兴奋的新功能,而且我现在仍然在学习。实际上,我学到的关于.NET的所有新东西都打开了我的眼界。
这听起来不可思议,但是大多数程序员开始编程并不是为了钱,而只是为了学习和建立某些东西所带来的成就感和满足感。对于我来说,C#和.NET框架使我找回了已经失去的热情。所以,我写这本书是为了与你分享我重新编程所感到的乐趣。我希望你在阅读本书并开始使用C#时同样感到愉快和兴奋。
本书的这个版本有什么新东西
如果你读过本书的第一版(谢谢!),你可能会奇怪这一版为什么厚了一倍。这是因为我写第一版的时间比较早。在.NET和C#刚发布之后的一段时期,写这方面的书的作者几乎都是在边学习边写作。而且与任何软件工程一样,你每学一点儿东西,就希望马上实践它。很明显,这对于书是不现实的,所以我知道需要编写第二版来使本书更完整。下面是对这一版的新内容的概述。我想你阅读完以下内容后就会理解我为什么这么渴望编写这一版了。
·大量的新资料,包括新添加的章节 读者对第一版提供了大量反馈意见,包括他们自己对C#语言的各个方面的体会,以及建议应该更深入地讨论哪些语言功能。作为对这些反馈的回应,我们重新审视了最常被问到的一些语言和框架功能,而且特意增强了本书对这些主题的讨论。许多章节经历了较大的修改,而且许多新添加的章节讨论了第一版没有涉及的问题。例如,应读者的请求,新添加的“安全”一章讨论了许多安全问题,包括代码访问安全、基于角色的安全、可检验的类型安全、代码签名、加密/数据签名和隔离存储。其他新添加的章包括“字符串处理和正则表达式”、“用流进行文件I/O"、“数值处理和Math类”、“集合和对象枚举”(我最喜欢的一章)、“固定和内存管理”、“从C#应用程序使用COM”和“在非托管代码中使用.NET组件”。另外, “使用XML进行文档记录”这个新章讨论了从C#源代码注释产生XML文档的能力。
·本书是一本真正的“技术内幕” 第二版最主要的目标是成为更加名副其实的“技术内幕”。我在第一版中犯的错误是,我只有在了解到MSIL可能影响我们使用C#编写代码的方式时才进入MSIL层。但是,许多读者很快指出,我们这些程序员都是很好奇的人,有时候我们希望了解某些东西只是因为我们不了解它们。在这一版的几乎每一章中,我都是先解释主题,然后深入研究编译器产生的MSIL。如果你花点儿时间研究幕后的情况,你会为某些功能的实现方式感到吃惊(至少我很吃惊)。另外,章节的组织方式使得不关心底层MSIL的人可以轻易跳过MSIL部分。另一方面,已经了解某个功能的语法并且知道如何使用它的人不必阅读相关的解释,而是可以直接跳到讨论底层MSIL的部分。
·“为什么”以及“如何” 许多书详细说明了使用语言功能所需的语法,然后给出一系列例子。但是你我可以从联机帮助中得到语法说明。我认为一本书提供的应该不止这些。因此,在每一章中我都试图解释给定的语言功能为什么存在,以及设计它是为了解决代码中的什么问题。毕竟,如果你从来没有使用过某种功能(比如接口或委托),那么不解释为什么需要使用它而只给出语法的话,你就无法充分利用它。
.更好且更现实的例子 我个人喜欢比foo/bar型例子更现实的例子,但是在第一版的示例应用程序中只有大约10%算得上是现实的例子。在本书中,这个比例是大约70%。例如,在第13章中提供了大量例子。前两个例子(操作符重载)讲解如何重载加法(+)操作符来对发票对象进行总计。另一个演示程序讲解如何重载几个操作符来创建一个便于产生渐变填充值的RGB类。然后,转换演示程序讲解如何转换各种温度制对象以及如何创建和使用一个三态控件。这些类不是完整的实现,但是我认为向程序员提供现实的演示程序有助于他们理解在应用程序中何处以及如何使用给定的功能。
谁应该阅读本书
本书的内容已经进行了较大的更新,添加了许多中高级信息,同时在组织各章内容时尽量使不熟悉C#的开发人员也能够理解这些主题。各章往往先解释C#或.NET的某个功能、语言中为什么需要这个功能以及它解决的问题。然后,提供一个与此功能相关的简单的演示应用程序,接着解释所需的语法。在讨论了这个主题的基础知识之后,提供一些比较高级的演示程序和资料,比如编译器产生的MSIL。因此,本书既适合C#和.NET的初学者,也适合已经使用过C#语言进行编程并且希望学习更高级的语言功能的开发人员。
对本书读者惟一的要求是,你们应该知道如何使用C、C++或Java编写简单的程序。另一个先决条件是你希望学习和研究使用C#语言编写应用程序的新技能。当然了,既然你手里拿着这本书,你显然是有这种愿望的。
本书的组织结构
本书被仔细地组织为三个在逻辑上相关的部分:每个部分由几章组成,每一章分别讨论C#或.NET开发的一个方面。
第一部分:C#类基本原理
在第一部分中,我们提供了使用C#定义和使用类和基本类成员的基础知识。但是,因为我的一个主要目标是提供非常实在的内容,所以我们没有谈论Microsoft的想法或我为什么认为.NET很出色等话题。程序员希望看到代码,而不是听我们大发议论。第1章直接带领你开始编写应用程序、编译库(DLL)和.NET模块以及创建多文件配件。你甚至将学习如何查看全局配件缓存并且使用ILDASM工具查看编译器产生的配件代码(MSIL)。第2章简单地概述了通用类型系统,第3章和第4章讲解C#类和结构定义的基础知识,以及如何定义和调用方法。这几章和书中其他章一样采用先易后难的组织形式,最后你会学到常量和只读字段的差异、类型初始化器的用途以及如何使用new和override关键字重定义虚拟方法。
第5章将这三个主题合在一起讨论,因为这些功能的目标都是使你能够为客户编写更直观的类接口。在讨论基础知识之后,你将通过许多底层MSIL来了解灵巧字段(特性)和灵巧数组(索引器)在幕后是如何实现的。第6章介绍我最喜欢的一个C#开发功能——属性。属性使你能够为类型定义文本性的标注,以后可以通过反射这些标注来判断类型的某些运行时特征。我们讨论了与属性相关的所有问题,包括属性的工作方式、如何在运行时查询一个类型是否具有属性以及如何获得属性的值。我们还讨论了预定义属性Conditional、Obsolete、CLSCompliant、DllImport、StructLayout以及各种配件属性。本书的第一部分以第7章结束,这一章讨论了如何声明和实现接口以及如何通过is和as操作符查询接口实现等主题。我们还讨论了如何显式地限定接口的成员名称、与接口继承相关的许多问题以及如何合并接口。
第二部分:编写代码
第一部分主要讲解如何定义和使用类和结构;第二部分讲述的都是与编程任务相关的内容。第8章和第9章分别讨论了如何使用表达式和操作符以及如何控制程序流。第10章是新添加的一章,它讨论的两个功能在使用任何语言进行编程时都是最重要的问题。第11章也是新增的,它集中讨论如何使用.NET框架提供的流类和文件系统类在磁盘上读写数据。你会用到该章中讨论的所有内容,包括字符串和二进制读取器以及使用BinaryFormatter、SoapFormatter和XMLSerializer对数据进行串行化。
我总是认为当今的程序设计书籍不应该将异常处理问题留到最后一章讨论,在.NET环境中更是如此。在.NET环境中异常处理用于跨语言界限传递错误信息,甚至COM错误也被自动映射为异常。第12章讲解了异常处理语法的基本知识、如何使用预定义的系统异常类以及如何定义自己的异常类。
对于我个人来说,只要一种语言功能能使我的类使用起来更直观,我就会喜欢它,而操作符重载功能正是如此。第13章不只讨论了操作符重载的语法和规则,还给出了两个适当进行操作符重载的现实例子:对发票进行总计和创建一个RGB类(它的操作符简化了在for循环中产生渐变值的过程)。接下来,我们在同一章中讨论了C#特有的功能:用户定义的转换。简单地说,用户定义的转换使你能够在结构或类上声明转换方式,这样结构或类就可以被转换为其他结构、类或基本的C#类型。
接下来是第14章。与接口和反射一样,这个主题也是对于.NET初学者最重要的一个主题。在这一章中,将学习如何定义和创建委托和多点委托,以及如何使用事件实现公布/预订(即观察者)模式。第二部分以另一个很酷的新章结束。这一章讨论C#特有的从C#源代码注释产生XML文档的功能。当然,这个功能的最大优点是它基于XML,这意味着它是完全开放的和可定制的(.NET框架的许多功能也具有这种性质)。
第三部分:高级C#
我写这一部分时心情最舒畅,因为它包含了一些我喜欢的主题。第16章介绍了数值处理和Math类。第17章讲解了如何使用IEnumerable和IEnumerator接口允许客户枚举你的类的数据。这一章中的高级主题包括创建有版本的枚举器、在允许枚举的同时保护数据以及实现可变接口。第18章讲解如何在应用程序中加人多线程和异步编程功能。单单一章是无法完全覆盖多线程这个主题的,但是我们还是讨论了开始使用线程所需的所有基本知识。在学习了如何创建、管理、调度和中止线程之后,将学习如何与线程进行数据交换。然后,我们将会看到几种串行化对线程方法的访问的办法(其目的是使方法成为线程安全的)。最后,将讲解如何使用委托启动异步方法,并且在方法返回之前阻塞其他操作,或者让此方法在完成时自动调用一个回调方法。第19章讨论了如何使用反射查询元数据。
第20章详细说明了内存管理和固定的知识,讨论的主题包括.NET垃圾收集器(GC)、Dispose方案、IDisposable接口、弱引用和编写不安全(未托管)的代码。接下来讨论COM互操作性问题,但是这个主题的内容太丰富以致于我使用了两章的篇幅。第21章讨论如何从.NET(C#)应用程序使用传统的COM组件;第22章讨论如何从Microsoft Visual C++和Microsoft Visual Basic客户(用COM编写)使用.NET组件(用C#编写)。这两章远远超出了初学者的能力范围,而且要求读者具有一定的COM经验。第三部分的最后一章(第23章)相当详细地讨论了安全程序设计问题,讨论的主题包括代码签名、密码服务、代码访问安全、基于角色的安全和隔离的存储。不要错过这一章。
附录:MSIL指令表
因为MSIL代码清单是本书的重要部分,所以本书的附录给出了一个完整的表格,它详细记录了每个MSIL指令、它的操作码、参数、说明和堆栈转换。堆栈转换使你能够看到执行指令之前和之后堆栈上与被调用的指令相关的内容。
本书中使用的惯例
与所有程序员一样,我也有自己喜欢的术语和编程风格。所以,下面对你将在书中看到的几个术语加以说明,并且解释几个惯例:
·“problem domain'’ 这个术语是我在多年前使用Coad/Yourdon面向对象的分析和设计 方法论时学到的。“problem domain'’是一个一般性术语,它是指要解决的问题(我们将这个术语译为“所涉及的问题”等——译者注)。
·“consumer”(使用者)和“client'’(客户) 这两个术语可以互换,它们代表使用类或类型的任何代码。
·“server”(服务器) 这个术语用于表示客户或使用者使用的一段代码(通常是一个类)。
·“argument”和“parameter” 与大多数程序员一样,我将这两个术语互换使用,都是指传递给方法的值(我们将这两个术语都译为“参数”——译者注)。
·“MSIL opcode”(MSIL操作码)和“MSIL instruction”(MSIL指令) 尽管这两个术语从技术上讲不完全相同,但在本书中,它们可以互换。
·“method prototype”(方法原型)和“method signature”(方法识别标志) 在本书中这两个术语可互换使用。
·分号 我有时用分号结束类定义,有时不这么做。与C++不同,C#编译器不检查类定义末尾的分号,所以你可以自由选择加不加分号。我开始使用分号只是因为我相信这有助于将同一个源代码文件中定义的多个类分隔开(只是在视觉效果上)。但这是个人选择,而且据我所知没有任何设计原则专门规定了这个问题。
·代码的换行 由于书的版心所限,源代码不得不加一些奇怪的换行。这很遗憾,但却是不可避免的。当你看到格式奇怪的代码行时请记住这一情况。
编译演示应用程序
本书的一个目标是使读者无需使用Microsoft Visual Studio.NET就可以编译所有的演示应用程序。这样,读者只要有C#编译器就能够编译和运行这些演示程序。但是,因为Visual Studio.NET是流行的C#应用程序编写工具,所以我们使用它创建大多数演示工程。最终结果是,你既可以使用Visual Studio.NET编译这些工程,也可以按照第1章的说明使用命令行编译器。
在这方面有几点需要注意,还有几处例外:
·所有Visual Studio工程 当使用Visual Studio.NET创建C#控制台应用程序时,产生两个源代码文件。第一个是主源代码文件Class1.cs。你将在第1章中学习如何将这个文件编译为应用程序。产生的第二个文件称为AssemblyInfo.cs。这个文件通常用于定义配件级属性。第1章介绍了配件,第6章讨论了属性。如果你使用命令行编译器,而且希望将Class1.cs和AssemblyInfo.cs文件编译为一个应用程序,那么只需使用以下的C#
编译器开关(其中的appname.exe是你给产生的应用程序起的名字):
csc/out:Class1.cs assemblyinfo.cs
·第1章 HelloWorldVS工程是这一章中使用Visual Studio.NET创建的惟一工程。
·第2章 这一章是对通用类型系统的简短概述,所以它不包含演示应用程序。
·第19章 Visual Studio.NET不支持创建用于组合成多文件配件的C#.NET模块。因此,CommProtocol演示应用程序只能从命令行编译。为了简化这个任务,我提供了一个简单的make文件(buildall.cmd),它对这个演示程序所需的所有组件进行编译。另外,为了简单起见,ILGeneration演示应用程序也只能从命令行编译。这个演示程序也有一个用于编译它的组件的buildall.cmd文件。
·第21章 这一章的主题是在C#应用程序中使用传统的COM组件,所以此章的几个演示应用程序是使用Visual Basic和Visual C++编译的。你需要使用Visual Studio编译这些工程。但是我有意使用了Visual C++6和Visual Basic 6。这样,如果你安装了Visual Studio 6和.NET Framework SDK(而没有安装Visual Studio.NET),那么仍然可以使用这些演示程序。另外,如果使用Visual Studio.NET打开这些文件,你只会得到一个消息,问你是否希望将这些工程转换为新格式。回答yes就可以正常运行它们了。
·第22章 这一章的主题是从未托管代码使用.NET组件。与第21章一样,某些演示程序是使用Visual C++6和Visual Basic 6编写的,所以也有前面所说明的问题。
关于附带的光盘
本书附带了一张光盘。如果你的Windows上启用了AutoRun功能,那么在将光盘放入光驱时你会看到一个快闪屏,它提供了一些安装选项。手工启动这个屏幕的办法是从光盘的根目录运行StartCD。StartCD程序提供了到光盘上包含的eBook(电子书)的链接、本书示例文件的安装程序和到光盘上包含的Microsoft.NET Framework SDK的链接。
本书的示例程序位于BookFiles\Code文件夹。你可以在光盘上查看这些示例,也可以使用StartCD中的安装程序将它们安装到硬盘上。
注意 如果你无法浏览Samples文件夹中的文件,那么可能是因为你使用的光盘驱动程序比较旧,不支持长文件名。如果是这种情况,必须通过运行Setup程序将示例文件安装到硬盘上,才能浏览这些文件。
Web上的示例代码
也可以从本书的网站(www.microsoft.com/MSPress/books/5861.asp)获得示例代码。在这个页面上还有代码升级和到相关书籍的链接。
系统需求
为了最充分地利用本书,我强烈建议你在阅读每一章的同时运行示例应用程序。为此需要·NET Framework SDK(其中包含C#编译器)。注意,如果你有Visual Studio.NET,Framework SDK就包含在其中。
另一个重要需求是:.NET代码可以在Windows 98和更高版本上执行,但是只有以下操作系统支持Framework SDK:
·Microsoft Windows NT 4.0
·Microsoft Windows 2000(建议使用Service Pack 2)
·Microsoft Windows XP(运行Microsoft ASP.NET需要Microsoft Windows XP Professional)
反馈
我并不认为“因为我写书,我就是专家”。我只是一个幸运地得到了写书机会的普通人。因此,我要向其他人学习而且实际上我也乐意这么做。如果你有关于本书的任何问题或者只是想与人谈谈C#和.NET,那么可以通过我的网站www.TheCodeChannel.com与我联系。我还在这个网站上提供了书籍的勘误表、补充章节(通常包含由于时间限制而没有放进本书的资料)和我使用C#编写的.NET应用程序示例。