logo资料库

C#本质论(中文版).pdf

第1页 / 共417页
第2页 / 共417页
第3页 / 共417页
第4页 / 共417页
第5页 / 共417页
第6页 / 共417页
第7页 / 共417页
第8页 / 共417页
资料共417页,剩余部分请下载后查看
继 承
4.1 继承的类型
4.1.1 实现继承和接口继承
4.1.2 多重继承
4.1.3 结构和类
4.2 实现继承
4.2.1 虚方法
4.2.2 隐藏方法
4.2.3 调用函数的基类版本
4.2.4 抽象类和抽象函数
4.2.5 密封类和密封方法
4.2.6 派生类的构造函数
1. 在层次结构中添加无参数的构造函数
2. 在层次结构中添加带参数的构造函数
4.3 修饰符
4.3.1 可见性修饰符
4.3.2 其他修饰符
4.4 接口
4.4.1 定义和实现接口
4.4.2 派生的接口
4.5 小结
数 组
5.1 简单数组
5.1.1 数组的声明
5.1.2 数组的初始化
5.1.3 访问数组元素
5.1.4 使用引用类型
5.2 多维数组
5.3 锯齿数组
5.4 Array类
5.4.1 属性
5.4.2 创建数组
5.4.3 复制数组
5.4.4 排序
5.5 数组和集合接口
5.5.1 IEumerable接口
5.5.2 ICollection接口
5.5.3 IList接口
5.6 枚举
5.6.1 IEnumerator接口
5.6.2 foreach语句
5.6.3 yield语句
5.7 小结
运算符和类型强制转换
6.1 运算符
6.1.1 运算符的简化操作
6.1.2 三元运算符
6.1.3 checked和unchecked运算符
6.1.4 is运算符
6.1.5 as运算符
6.1.6 sizeof运算符
6.1.7 typeof运算符
6.1.8 可空类型和运算符
6.1.9 空接合运算符
6.1.10 运算符的优先级
6.2 类型的安全性
6.2.1 类型转换
1. 隐式转换方式
2. 显式转换方式
6.2.2 装箱和拆箱
6.3 对象的相等比较
6.3.1 引用类型的相等比较
1. ReferenceEquals()方法
2. 虚拟的Equals()方法
3. 静态的Equals()方法
4. 比较运算符==
6.3.2 值类型的相等比较
6.4 运算符重载
6.4.1 运算符的工作方式
6.4.2 运算符重载的示例:Vector结构
1. 添加更多的重载
2. 比较运算符的重载
3. 可以重载的运算符
6.5 用户定义的数据类型转换
6.5.1 执行用户定义的类型转换
1. 类之间的数据类型转换
2. 基类和派生类之间的数据类型转换
3. 装箱和拆箱数据类型转换
6.5.2 多重数据类型转换
6.6 小结
委托和事件
7.1 委托
7.1.1 在C#中声明委托
7.1.2 在C#中使用委托
7.2 委托推断
7.3 匿名方法
7.3.1 简单的委托示例
7.3.2 BubbleSorter示例
7.3.3 多播委托
7.4 事件
7.4.1 从客户的角度讨论事件
7.4.2 生成事件
7.5 小结
字符串和正则表达式
8.1 System.String类
8.1.1 创建字符串
8.1.2 StringBuilder成员
8.1.3 格式化字符串
1. 字符串的格式化
2. FormattableVector示例
8.2 正则表达式
8.2.1 正则表达式概述
8.2.2 RegularExpressionsPlayaround示例
8.2.3 显示结果
8.2.4 匹配、组合和捕获
8.3 小结
泛 型
9.1 概述
9.1.1 性能
9.1.2 类型安全
9.1.3 二进制代码的重用
9.1.4 代码的扩展
9.1.5 命名约定
9.2 创建泛型类
9.3 泛型类的特性
9.3.1 默认值
9.3.2 约束
9.3.3 继承
9.3.4 静态成员
9.4 泛型接口
9.5 泛型方法
9.6 泛型委托
9.6.1 执行委托调用的方法
9.6.2 对Array类使用泛型委托
9.7 Framework的其他泛型类型
9.7.1 结构Nullable
9.7.2 EventHandler
9.7.3 ArraySegment
9.8 小结
集 合
10.1 集合接口和类型
10.2 列表
10.2.1 创建列表
1. 添加元素
2. 插入元素
3. 访问元素
4. 删除元素
5. 搜索
6. 排序
7. 类型转换
10.2.2 只读集合
10.3 队列
10.4 栈
10.5 链表
10.6 有序表
10.7 字典
10.7.1 键的类型
10.7.2 字典示例
10.7.3 其他字典类
10.8 带多个键的字典
10.9 位数组
10.9.1 BitArray
10.9.2 BitVector32
10.10 性能
10.11 小结
内存管理和指针
11.1 后台内存管理
11.1.1 值数据类型
11.1.2 引用数据类型
11.1.3 垃圾收集
11.2 释放未托管的资源
11.2.1 析构函数
11.2.2 IDisposable接口
11.2.3 实现IDisposable接口和析构函数
11.3 不安全的代码
11.3.1 指针
1. 编写不安全的代码
2. 指针的语法
3. 将指针转换为整数类型
4. 指针类型之间的转换
5. void指针
6. 指针的算法
7. sizeof运算符
8. 结构指针:指针成员访问运算符
9. 类成员指针
11.3.2 指针示例:PointerPlayaround
11.3.3 使用指针优化性能
1. 创建基于堆栈的数组
2. 示例QuickArray
11.4 小结
反 射
12.1 定制特性
12.1.1 编写定制特性
1. AttributeUsage特性
2. 指定特性参数
3. 指定特性的可选参数
12.1.2 定制特性示例:WhatsNewAttributes
1. WhatsNewAttributes 库程序集
2. VectorClass程序集
12.2 反射
12.2.1 System.Type类
1. Type的属性
2. 方法
12.2.2 TypeView示例
12.2.3 Assembly类
1. 查找在程序集中定义的类型
2. 查找定制特性
12.2.4 完成WhatsNewAttributes示例
12.3 小结
错误和异常
13.1 错误和异常处理
13.1.1 异常类
异常基类
13.1.2 捕获异常
1. 执行多个catch块
2. 在其他代码中捕获异常
3. System.Exception属性
4. 没有处理异常时所发生的情况
5. 嵌套的 try块
13.1.3 用户定义的异常类
1. 捕获用户定义的异常
2. 抛出用户定义的异常
3. 定义异常类
13.2 小结
Visual Studio 2005
14.1 使用Visual Studio 2005
14.1.1 创建项目
1. 选择项目类型
2. 新建的控制台项目
3. 其他文件的创建
14.1.2 解决方案和项目
1. 给解决方案添加另一个项目
2. 设置启动项目
14.1.3 Windows应用程序代码
14.1.4 读取Visual Studio 6项目
14.1.5 项目的浏览和编码
1. 可折叠的编辑器
2. 其他窗口
3. pin按钮
14.1.6 生成项目
1. 生成、编译和产生项目
2. 调试和发布项目
3. 选择配置
4. 编辑配置
14.1.7 调试
1. 断点
2. 监视点
3. 异常
14.2 修订功能
14.3 Visual Studio 2005 for .NET Framework 3.0
14.3.1 .NET 3.0的项目类型
14.3.2 在Visual Studio中建立WPF应用程序
14.3.3 在Visual Studio中建立WF应用程序
14.4 小结
部 署
15.1 部署的设计
15.2 部署选项
15.2.1 Xcopy实用工具
15.2.2 Copy Web工具
15.2.3 发布Web站点
15.2.4 部署项目
15.2.5 ClickOnce
15.3 部署的要求
15.4 部署.NET运行库
15.5 简单的部署
15.5.1 Xcopy部署
15.5.2 Xcopy和Web应用程序
15.5.3 Copy Web工具
15.5.4 发布Web站点
15.6 Installer项目
15.6.1 Windows Installer
15.6.2 创建安装程序
1. 简单的客户应用程序
2. 简单的Web应用程序
3. Web服务器上的客户
15.7 ClickOnce
15.7.1 ClickOnce操作
15.7.2 发布应用程序
15.7.3 ClickOnce设置
15.7.4 应用程序缓存
15.7.5 安全性
15.7.6 高级选项
1. 文件系统编辑器
2. 注册编辑器
3. 文件类型编辑器
4. 用户界面编辑器
5. 定制动作编辑器
6. 启动条件编辑器
15.8 小结
第 章 1 .NET 体系结构 我们不能孤立地使用 C#语言,而必须和.NET Framework 一起考虑。C#编译器专门用 于.NET,这表示用 C#编写的所有代码总是在.NET Framework 中运行。对于 C#语言来说, 可以得出两个重要的结论: ● C#的结构和方法论反映了.NET 基础方法论。 ● 在许多情况下,C#的特定语言功能取决于.NET 的功能,或依赖于.NET 基类。 由于这种依赖性,在开始使用 C#编程前,了解.NET 的结构和方法论就非常重要了, 这就是本章的目的。 本章首先了解在.NET 编译和运行所有的代码(包括 C#)时通常会出现什么情况。对这 些内容进行概述之后,就要详细阐述 Microsoft 中间语言(Microsoft Intermediate Language, MSIL 或简称为 IL),所有编译好的代码都要使用这种语言。本章特别要介绍 IL、通用类型 系统(Common Type System,CTS)及公共语言规范(Common Language Specification,CLS) 如何提供.NET 语言之间的互操作性。最后解释各种语言如何使用.NET,包括 Visual Basic 和 C++。 之后,我们将介绍.NET 的其他特性,包括程序集、命名空间和.NET 基类。最后本章 简要探讨一下 C#开发人员可以创建的应用程序类型。 1.1 C#与.NET 的关系 C#是一种相当新的编程语言,C#的重要性体现在以下两个方面: ● 它是专门为与 Microsoft 的.NET Framework 一起使用而设计的。(.NET Framework 是一个功能非常丰富的平台,可开发、部署和执行分布式应用程序)。 ● 它是一种基于现代面向对象设计方法的语言,在设计它时,Microsoft 还吸取了其 他类似语言的经验,这些语言是近 20 年来面向对象规则得到广泛应用后才开发出 来的。 有一个很重要的问题要弄明白:C#就其本身而言只是一种语言,尽管它是用于生成面
第Ⅰ部分 C# 语 言 向.NET 环境的代码,但它本身不是.NET 的一部分。.NET 支持的一些特性,C#并不支持。 而 C#语言支持的另一些特性,.NET 却不支持(例如运算符重载)! 但是,因为 C#语言是和.NET 一起使用的,所以如果要使用 C#高效地开发应用程序, 理解 Framework 就非常重要,所以本章将介绍.NET 的内涵。 1.2 公共语言运行库 .NET Framework 的核心是其运行库的执行环境,称为公共语言运行库(CLR)或.NET 运行库。通常将在 CLR 的控制下运行的代码称为托管代码(managed code)。 但是,在 CLR 执行编写好的源代码之前,需要编译它们(在 C#中或其他语言中)。在.NET 中,编译分为两个阶段: (1) 把源代码编译为 Microsoft 中间语言(IL)。 (2) CLR 把 IL 编译为平台专用的代码。 这个两阶段的编译过程非常重要,因为 Microsoft 中间语言(托管代码)是提供.NET 的 许多优点的关键。 托管代码的优点 Microsoft 中间语言与 Java 字节代码共享一种理念:它们都是低级语言,语法很简单(使 用数字代码,而不是文本代码),可以非常快速地转换为内部机器码。对于代码来说,这种 精心设计的通用语法,有很重要的优点。 1. 平台无关性 首先,这意味着包含字节代码指令的同一文件可以放在任一平台中,运行时编译过程 的最后阶段可以很容易完成,这样代码就可以运行在特定的平台上。换言之,编译为中间 语言就可以获得.NET 平台无关性,这与编译为 Java 字节代码就会得到 Java 平台无关性是 一样的。 注意.NET 的平台无关性目前只是一种可能,因为在编写本书时,.NET 只能用于 Windows 平台,但人们正在积极准备,使它可以用于其他平台(参见 Mono 项目,它用于实 现.NET 的开放源代码,参见 http://www.go-mono.com/)。 2. 提高性能 前面把 IL 和 Java 做了比较,实际上,IL 比 Java 字节代码的作用还要大。IL 总是即时 编译的(称为 JIT 编译),而 Java 字节代码常常是解释性的,Java 的一个缺点是,在运行应 用程序时,把 Java 字节代码转换为内部可执行代码的过程会导致性能的损失(但在最近, Java 在某些平台上能进行 JIT 编译)。 JIT 编译器并不是把整个应用程序一次编译完(这样会有很长的启动时间),而是只编译 它调用的那部分代码(这是其名称由来)。代码编译过一次后,得到的内部可执行代码就存 储起来,直到退出该应用程序为止,这样在下次运行这部分代码时,就不需要重新编译了。 4
第 1 章 .NET 体系结构 Microsoft 认为这个过程要比一开始就编译整个应用程序代码的效率高得多,因为任何应用 程序的大部分代码实际上并不是在每次运行过程中都执行。使用 JIT 编译器,从来都不会 编译这种代码。 这解释了为什么托管 IL 代码的执行几乎和内部机器代码的执行速度一样快,但是并没 有说明为什么 Microsoft 认为这会提高性能。其原因是编译过程的最后一部分是在运行时进 行的,JIT 编译器确切地知道程序运行在什么类型的处理器上,可以利用该处理器提供的 任何特性或特定的机器代码指令来优化最后的可执行代码。 传统的编译器会优化代码,但它们的优化过程是独立于代码所运行的特定处理器的。 这是因为传统的编译器是在发布软件之前编译为内部机器可执行的代码。即编译器不知道 代码所运行的处理器类型,例如该处理器是 x86 兼容处理器还是 Alpha 处理器,这超出了 基本操作的范围。例如 Visual Studio 6 为一般的奔腾机器进行了优化,所以它生成的代码 就不能利用奔腾 III 处理器的硬件特性。相反,JIT 编译器不仅可以进行 Visual Studio 6 所 能完成的优化工作,还可以优化代码所运行的特定处理器。 3. 语言的互操作性 使用 IL 不仅支持平台无关性,还支持语言的互操作性。简而言之,就是能将任何一种 语言编译为中间代码,编译好的代码可以与从其他语言编译过来的代码进行交互操作。 那么除了 C#之外,还有什么语言可以通过.NET 进行交互操作呢?下面就简要讨论其 他常见语言如何与.NET 交互操作。 (1) Visual Basic 2005 Visual Basic 6 在升级到 Visual Basic .NET 2002 时,经历了一番脱胎换骨的变化,才集 成到.NET Framework 的第一版中。Visual Basic 语言对 Visual Basic 6 进行了很大的演化, 也就是说,Visual Basic 6 并不适合运行.NET 程序。例如,它与 COM 的高度集成,且只把 事件处理程序作为源代码显示给开发人员,大多数后台代码不能用作源代码。另外,它不 支持继承,Visual Basic 使用的标准数据类型也与.NET 不兼容。 Visual Basic 6 在 2002 年升级为 Visual Basic .NET,对 Visual Basic 进行的改变非常大, 完全可以把 Visual Basic 当作是一种新语言。现有的 Visual Basic 6 代码不能编译为 Visual Basic 2005 代码(或 Visual Basic .NET 2002 和 2003 代码),把 Visual Basic 6 程序转换为 Visual Basic 2005 时,需要对代码进行大量的改动,但大多数修改工作都可以由 Visual Studio 2005(Visual Studio 的升级版本,用于与.NET 一起使用)自动完成。如果把 Visual Basic 6 项 目读到 Visual Studio 2005 中,Visual Studio 2005 就会升级该项目,也就是说把 Visual Basic 6 源代码重写为 Visual Basic 2005 源代码。虽然这意味着其中的工作已大大减轻,但用户 仍需要检查新的 Visual Basic 2005 代码,以确保项目仍可正确工作,因为这种转换并不十 分完美。 这种语言升级的一个副作用是不能再把 Visual Basic 2005 编译为内部可执行代码了。 Visual Basic 2005 只编译为中间语言,就像 C#一样。如果需要继续使用 Visual Basic 6 编写 程序,就可以这么做,但生成的可执行代码会完全忽略.NET Framework,如果继续把 Visual Studio 作为开发环境,就需要安装 Visual Studio 6。 5
第Ⅰ部分 C# 语 言 (2) Visual C++ 2005 Visual C++ 6 有许多 Microsoft 对 Windows 的特定扩展。通过 Visual C++ .NET,又加 入了更多的扩展内容,来支持.NET Framework。现有的 C++源代码会继续编译为内部可执 行代码,不会有修改,但它会独立于.NET 运行库运行。如果让 C++代码在.NET Framework 中运行,就可以在代码的开头添加下述命令: #using 还可以把标记/clr 传递给编译器,这样编译器假定要编译托管代码,因此会生成中间 语言,而不是内部机器码。C++的一个有趣的问题是在编译托管代码时,编译器可以生成 包含内嵌本机可执行代码的 IL。这表示在 C++代码中可以把托管类型和非托管类型合并起 来,因此托管 C++代码: class MyClass { 定义了一个普通的 C++类,而代码: __gc class MyClass { 生成了一个托管类,就好像使用 C#或 Visual Basic 2005 编写类一样。实际上,托管 C++比 C#更优越的一点是可以在托管 C++代码中调用非托管 C++类,而不必采用 COM 交 互功能。 如果在托管类型上试图使用.NET 不支持的特性(例如,模板或类的多继承),编译器就 会出现一个错误。另外,在使用托管类时,还需要使用非标准的 C++特性(例如上述代码中 的__gc 关键字)。 因为 C++允许低级指针操作,C++编译器不能生成可以通过 CLR 内存类型安全测试的 代码。如果 CLR 把代码标识为内存类型安全是非常重要的,就需要用其他一些语言编写源 代码,例如 C# 或 Visual Basic 2005。 (3) Visual J# 2005 最新添加的语言是 Visual J# 2005。在.NET Framework 1.1 版本推出之前,用户必须下 载相应的软件,才能使用 J#。现在 J#语言内置于.NET Framework 中。因此,J#用户可以利 用 Visual Studio 2005 的所有常见特性。Microsoft 希望大多数 J++用户认为他们在使用.NET 时,将很容易使用 J#。J#不使用 Java 运行库,而是使用与其他.NET 兼容语言一样的基类 库。这说明,与 C#和 Visual Basic 2005 一样,可以使用 J#创建 ASP.NET Web 应用程序、 Windows 窗体、XML Web 服务和其他应用程序。 (4) 脚本语言 脚本语言仍在使用之中,但由于.NET 的推出,它们的重要性在降低。与此同时,JScript 升级到了 JScript.NET。现在 ASP.NET 页面可以用 JScript.NET 编写,可以把 JScript.NET 当作一种编译语言来运行,而不是解释性的语言,也可以编写强类型化的 JScript.NET 代码。 有了 ASP.NET 后,就没有必要在服务器端的 Web 页面上使用脚本语言了,但 VBA 仍用作 Office 文档和 Visual Studio 宏语言。 6
第 1 章 .NET 体系结构 (5) COM 和 COM+ 从技术上讲,COM 和 COM+并不是面向.NET 的技术,因为基于它们的组件不能编 译为 IL(但如果原来的 COM 组件是用 C++编写的,使用托管 C++,在某种程度上可以这么 做)。但是,COM+仍然是一个重要的工具,因为其特性没有在.NET 中完全实现。另外, COM 组件仍可以使用——.NET 组合了 COM 的互操作性,从而使托管代码可以调用 COM 组件,COM 组件也可以调用托管代码(见第 33 章)。在一般情况下,把新组件编写为.NET 组件,大多是为了方便,因为这样可以利用.NET 基类和托管代码的其他优点。 1.3 中间语言 通过前面的学习,我们理解了 Microsoft 中间语言显然在.NET Framework 中有非常重 要的作用。C#开发人员应明白,C#代码在执行前要编译为中间语言(实际上,C#编译器仅 编译为托管代码),这是有意义的,现在应详细讨论一下 IL 的主要特征,因为面向.NET 的 所有语言在逻辑上都需要支持 IL 的主要特征。 下面就是中间语言的主要特征: ● 面向对象和使用接口 ● 值类型和引用类型之间的巨大差别 ● 强数据类型 ● 使用异常来处理错误 ● 使用特性(attribute) 下面详细讨论这些特征。 1.3.1 面向对象和接口的支持 .NET 的语言无关性还有一些实际的限制。中间语言在设计时就打算实现某些特殊的编 程方法,这表示面向它的语言必须与编程方法兼容,Microsoft 为 IL 选择的特定道路是传 统的面向对象的编程,带有类的单一继承性。 注意: 不 熟悉 面向对 象概 念的读 者应 参考附 录 A,获得 更多 的信息 。附 录 A 可 以 从 www.wrox.com 上下载。 除了传统的面向对象编程外,中间语言还引入了接口的概念,它们显示了在带有 COM 的 Windows 下的第一个实现方式。.NET 接口与 COM 接口不同,它们不需要支持任何 COM 基础结构,例如,它们不是派生自 IUnknown,也没有对应的 GUID。但它们与 COM 接口 共享下述理念:提供一个契约,实现给定接口的类必须提供该接口指定的方法和属性的实 现方式。 前面介绍了使用.NET 意味着要编译为中间语言,即需要使用传统的面向对象的方法来 编程。但这并不能提供语言的互操作性。毕竟,C++和 Java 都使用相同的面向对象的范型, 但它们仍不是可交互操作的语言。下面需要详细探讨一下语言互操作性的概念。 7
第Ⅰ部分 C# 语 言 首先,需要确定一下语言互操作性的含义。毕竟,COM 允许以不同语言编写的组件 一起工作,即可以调用彼此的方法。这就足够了吗?COM 是一个二进制标准,允许组件 实例化其他组件,调用它们的方法或属性,而无需考虑编写相关组件的语言。但为了实现 这个功能,每个对象都必须通过 COM 运行库来实例化,通过接口来访问。根据相关组件 的线程模型,不同线程上内存空间和运行组件之间要编组数据,这还可能造成很大的性能 损失。在极端情况下,组件保存为可执行文件,而不是 DLL 文件,还必须创建单独的进程 来运行它们。重要的是组件要能与其他组件通信,但仅通过 COM 运行库进行通信。无论 COM 是用于允许使用不同语言的组件直接彼此通信,或者创建彼此的实例,系统都把 COM 作为中间件来处理。不仅如此,COM 结构还不允许利用继承实现,即它丧失了面向对象 编程的许多优势。 一个相关的问题是,在调试时,仍必须单独调试用不同语言编写的组件。这样就不可 能在调试器上调试不同语言的代码了。语言互操作性的真正含义是用一种语言编写的类应 能直接与用另一种语言编写的类通信。特别是: ● 用一种语言编写的类应能继承用另一种语言编写的类。 ● 一个类应能包含另一个类的实例,而不管它们是使用什么语言编写的。 ● 一个对象应能直接调用用其他语言编写的另一个对象的方法。 ● 对象(或对象的引用)应能在方法之间传递。 ● 在不同的语言之间调用方法时,应能在调试器中调试这些方法调用,即调试不同 语言编写的源代码。 这是一个雄心勃勃的目标,但令人惊讶的是,.NET 和中间语言已经实现了这个目标。 在调试器上调试方法时,Visual Studio 2005 IDE 提供了这样的工具(不是 CLR 提供的)。 1.3.2 相异值类型和引用类型 与其他编程语言一样,中间语言提供了许多预定义的基本数据类型。它的一个特性是 值类型和引用类型有明显的区别。对于值类型,变量直接保存其数据,而对于引用类型, 变量仅保存地址,对应的数据可以在该地址中找到。 在 C++中,引用类型类似于通过指针来访问变量,而在 Visual Basic 中,与引用类型 最相似的是对象,Visual Basic 6 总是通过引用来访问对象。中间语言也有数据存储的规范: 引用类型的实例总是存储在一个名为“托管堆”的内存区域中,值类型一般存储在堆栈中(但 如果值类型在引用类型中声明为字段,它们就内联存储在堆中)。第 2 章“C#基础”讨论 堆栈和堆,及其工作原理。 1.3.3 强数据类型 中间语言的一个重要方面是它基于强数据类型。所有的变量都清晰地标记为属于某个 特定数据类型(在中间语言中没有 Visual Basic 和脚本语言中的 Variant 数据类型)。特别是 中间语言一般不允许对模糊的数据类型执行任何操作。 例如,Visual Basic 6 开发人员习惯于传递变量,而无需考虑它们的类型,因为 Visual 8
第 1 章 .NET 体系结构 Basic 6 会自动进行所需的类型转换。C++开发人员习惯于在不同类型之间转换指针类型。 执行这类操作将大大提高性能,但破坏了类型的安全性。因此,这类操作只能在某些编译 为托管代码的语言中的特殊情况下进行。确实,指针(相对于引用)只能在标记了的 C#代码 块中使用,但在 Visual Basic 中不能使用(但一般在托管 C++中允许使用)。在代码中使用指 针会立即导致 CLR 提供的内存类型安全性检查失败。 注意,一些与.NET 兼容的语言,例如 Visual Basic 2005,在类型化方面的要求仍比较 松,但这是可以的,因为编译器在后台确保在生成的 IL 上强制类型安全。 尽管强迫实现类型的安全性最初会降低性能,但在许多情况下,我们从.NET 提供的、 依赖于类型安全的服务中获得的好处更多。这些服务包括: ● 语言的互操作性 ● 垃圾收集 ● 安全性 ● 应用程序域 下面讨论强数据类型化对这些.NET 特性非常重要的原因。 1. 语言互操作性中强数据类型的重要性 如果类派生自其他类,或包含其他类的实例,它就需要知道其他类使用的所有数据类 型,这就是强数据类型非常重要的原因。实际上,过去没有任何系统指定这些信息,从而 成为语言继承和交互操作的真正障碍。这类信息不只是在一个标准的可执行文件或 DLL 中出现。 假定将 Visual Basic 2005 类中的一个方法定义为返回一个整型——Visual Basic 2005 可 以使用的标准数据类型之一。但 C#没有该名称的数据类型。显然,我们只能从该类中派生, 再使用这个方法,如果编译器知道如何把 Visual Basic 2005 的整型类型映射为 C#定义的某 种已知类型,就可以在 C#代码中使用返回的类型。这个问题在.NET 中是如何解决的? (1) 通用类型系统(CTS) 这个数据类型问题在.NET 中使用通用类型系统(CTS)得到了解决。CTS 定义了可以在 中间语言中使用的预定义数据类型,所有面向.NET Framework 的语言都可以生成最终基于 这些类型的编译代码。 例如,Visual Basic 2005 的整型实际上是一个 32 位有符号的整数,它实际映射为中间 语言类型 Int32。因此在中间语言代码中就指定这种数据类型。C#编译器可以使用这种类 型,所以就不会有问题了。在源代码中,C#用关键字 int 来表示 Int32,所以编译器就认为 Visual Basic 2005 方法返回一个 int 类型的值。 通用类型系统不仅指定了基本数据类型,还定义了一个内容丰富的类型层次结构,其 中包含设计合理的位置,在这些位置上,代码允许定义它自己的类型。通用类型系统的层 次结构反映了中间语言的单一继承的面向对象方法,如图 1-1 所示。 9
第Ⅰ部分 C# 语 言 类型 引用类型 接口类型 内置值类型 值类型 枚举 用户定义 的值类型 指针类型 自我描述类型 类类型 数组 委托 装箱的值类型 用户定义的 引用类型 这个树形结构中的类型说明如表 1-1 所示。 图 1-1 类 型 Type Value Type Reference Types Built-in Value Types Enumerations 表 1-1 含 义 代表任何类型的基类 代表任何值类型的基类 通过引用来访问,且存储在堆中的任何数据类型 包含大多数标准基本类型,可以表示数字、Boolean 值或字符 枚举值的集合 User-defined Value Types 在源代码中定义,且保存为值类型的数据类型。在 C#中,它表示结构 Interface Types Pointer Types 接口 指针 Self-describing Types 为垃圾回收器提供对它们本身有益的信息的数据类型(参见下一节) Arrays Class Types 包含对象数组的类型 可自我描述的类型,但不是数组 Delegates User-definedReference Types 在源代码中定义,且保存为引用类型的数据类型。在 C#中,它表示类 用于把引用包含在方法中的类型 Boxed Value Types 值类型,临时打包放在一个引用中,以便于存储在堆中 这里没有列出内置的所有值类型,因为第 3 章将详细介绍它们。在 C#中,编译器识别 10
分享到:
收藏