《C#使用手册_教程.docx》由会员分享,可在线阅读,更多相关《C#使用手册_教程.docx(196页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、第1多.NET体多信构我们不能孤立地使用C#语言,而必须和.NET Framework 一起考虑。C# 编译器专门用于.NET,这表示用C#编写的所有代码总是在NET Framewo rk中运行。对于C#语言来说,可以得出两个重要的结论: C#的结构和方法论反映了.NET基础方法论。 在许多情况下,C#的特定语言功能取决于.NET的功能,或依赖于.NET基类。由于这种依赖性,在开始使用C#编程前,了解.NET的结构和方法论就非 常重要了,这就是本章的目的。本章首先了解在.NET编译和运行所有的代码(包括C#)时通常会出现什么 情况。对这些内容进行概述之后,就要详细阐述Microsoft中间语言
2、(Micro soft Intermediate Language, MSIL或简称为IL),所有编译好的代码都要使 用这种语言。本章特别萋介绍IL、通用类型束统(Common Type System, CTS)及公共语言规范(Common Language Specification, CLS)如何提供.NET 语言之间的互操作性。最后解释各种语言如何使用.NET,包括Visual Basi c 和 C+。之后,我们将介绍.NET的其他特性,包括程序集、命名空间和.NET基类。 最后本章简要探讨一下C#开发人员可以创建的应用程序类型。1.1 C#与.NET的关系C#是种相当新的编程语言,C#
3、的重要性体现在以下两个方面:它是专门为与Microsoft的.NET Framework 一起使用而设计的。(.NET Framework是个功能非常丰富的平台,可开发、部署 和执行分布式应用程序)。它是种基于现代面向对象设计方法的语言,在设计它时,Microsoft还吸取了其他类似语言的经验,这些语言是近20年来面向 对象规则得到广泛应用后开发出来的。有一个很重要的问题要弄明白:C#就其本身而言只是种语言,尽管它是 用于生成面向.NET环境的代码,但它本身不是.NET的一部分。.NET支 持的些特性,C#并不支持。而C#语言支持的另一些特性,.NET却不支 持(例如运算符重载)!但是,因为C
4、#语言是和.NET 一起使用的,所以如果要使用C#高效地开 发应用程序,理解Framework就非常重要,所以本章将介绍.NET的内涵。1.2 公共语言运行库.NET Framework的核心是其运行库的执行环境,称为公共语言运行库(CL R)或.NET运行库。通常将在CLR的控制下运行的代码称为托管代码(mana ged code) 但是,在CLR执行开发的源代码之前,需要编译它们(在C#中或其他语言 中)。在NET中,编译分为两个阶段:(1)把源代码编译为Microsoft中间语言(IL)。(2) CLR把IL编译为平台专用的代码。这个两阶段的编译过程非常重要,因为Microsoft中间语
5、言(托管代码)是提 供.NET的许多优点的关键。托管代码的优点Microsoft中间语言与Java字节代码共享一种理念:它们都是种低级语言, 语法很简单(使用数字代码,而不是文本代码),可以非常快速地转换为内 部机器码。对于代码来说,这种精心设计的通用语法,有很重要的优点。1 .平台无关性首先,这意味着包含字节代码指令的同一文件可以放在任一平台中,运行 时编译过程的最后阶段可以很容易完成,这样代码就可以运行在该特定的 平台上。换言之,编译为中间语言就可以获得.NET平台无关性,这与编 译为Java字节代码就会得到Java平台无关性是样的。注意.NET的平台无关性目前只是种可能,因为在编写本书时
6、,.NET只 能用于Windows平台,但人们正在积极准备,使它可以用于其他平台(参 见Mono项目,它用于创建.NET的开放源代码的实现,参见http:/www. go-mono, com/) 2 .提高性能前面把IL和Java做了比较,实际上,IL比Java字节代码的作用还要大。I L总是即时编译的(称为JIT编译),而Java字节代码常常是解释性的,Java 的个缺点是,在运行应用程序时,把Java字节代码转换为内部可执行代 码的过程会导致性能的损失(但在最近,Java在某些平台上能进行JIT编译)。 JIT编译器并不是把整个应用程序一次编译完(这样会有很长的启动时间), 而是只编译它调
7、用的那部分代码(这是其名称由来)。代码编译过一次后, 得到的内部可执行代码就存储起来,直到退出该应用程序为止,这样在下 次运行这部分代码时,就不需要重新编译了。Microsoft认为这个过程要比 开始就编译整个应用程序代码的效率高得多,因为任何应用程序的大部 分代码实际上并不是在每次运行过程中都执行。使用JIT编译器,从来都 不会编译这种代码。这解释了为什么托管IL代码的执行几乎和内部机器代码的执行速度样 快,但是并没有说明为什么Microsoft认为这会提高性能。其原因是编译过 程的最后一部分是在运行时进行的,JIT编译器确切地知道程序运行在什 么类型的处理器上,可以利用该处理器提供的任何特
8、性或特定的机器代码 指令来优化最后的可执行代码。传统的编译器会优化代码,但它们的优化过程是独立于代码所运行的特定 处理器的。这是因为传统的编译器是在发布软件之前编译为内部机器可执 行的代码。即编译器不知道代码所运行的处理器类型,例如该处理器是x86兼容处理器还是Alpha处理器,这超出了基本操作的范围。例如Visual Studio 6为一般的Pentium机器进行了优化,所以它生成的代码就不能利 用Pentium III处理器的硬件特性。相反,JIT编译器不仅可以进行Visual Studio 6所能完成的优化工作,还可以优化代码所运行的特定处理器。3 .语言的互操作性使用IL不仅支持平台无
9、关性,还支持语言的互操作性。简言之,就是能将 任何种语言编译为中间代码,编译好的代码可以与从其他语言编译过来 的代码进行交互操作。那么除了 C#之外,还有什么语言可以通过.NET进行交互操作呢?下面就 简要讨论其他常见语言如何与.NET交互操作。(1) Visual Basic 2005Visual Basic 6在升级到Visual Basic .NET 2002时,经历了 一番脱胎换骨 的变化,集成到.NET Framework的第一版中。Visual Basic语言对Visu al Basic 6进行了很大的演化,也就是说,Visual Basic 6并不适合运行.NE T程序。例如,它
10、与COM的高度集成,且只把事件处理程序作为源代码 显示给开发人员,大多数后台代码不能用作源代码。另外,它不支持继承, Visual Basic使用的标准数据类型也与.NET不兼容。Visual Basic 6 在 2002 年升级为 Visual Basic .NET,对 Visual Basic 进行的 改变非常大,完全可以把Visual Basic当作是种新语言。现有的Visual B asic 6 代码不能编译为 Visual Basic 2005 代码(或 Visual Basic .NET 2002 和 2003代码),把Visual Basic 6程序转换为Visual Basic
11、 2005时,需要对代 码进行大量的改动,但大多数修改工作需可以由Visual Studio 2005(Visual Studio的升级版本,用于与.NET 一起使用)自动完成。如果要把个Visua 1 Basic 6 项目读取到 Visual Studio 2005 中,Visual Studio 2005 就会升级该 项目,也就是说把Visual Basic 6源代码重写为Visual Basic 2005源代码。 虽然这意味着其中的工作已大大减轻,但用户仍需要检查新的Visual Basi c 2005以确保项目仍可正确工作,因为这种转换并不十分完美。这种语言升级的个副作用是不能再把Vi
12、sual Basic 2005编译为内部可执 行代码了。Visual Basic 2005只编译为中间语言,就像C#样。如果需要 继续使用Visual Basic 6编写程序,就可以这么做,但生成的可执行代码会 完全忽略.NET Framework,如果继续把Visual Studio作为开发环境,就需 要安装 Visual Studio 6(2) Visual C+ 2005Visual C+ 6 有许多 Microsoft Windows 的特定扩展。通过 Visual C+ . NET,又加入了更多的扩展内容,来支持.NET Framework。现有的C+ 源代码会继续编译为内部可执行代
13、码,不会有修改,但它会独立于.NET 运行库运行。如果要让C+代码在.NET Framework中运行,就要在代码 的开头添加下述命令:fusing 还可以把标记/clr传递给编译器,这样编译器假定要编译托管代码,因此 会生成中间语言,而不是内部机器码。C+的个有趣的问题是在编译托 管代码时,编译器可以生成包含内嵌本机可执行代码的IL。这表示在C+ +代码中可以把托管类型和非托管类型合并起来,因此托管C+代码:class MyClass定义了一个普通的C+类,而代码: _gc class MyClass生成了一个托管类,就好像使用C#或Visual Basic 2005编写类样。实际 上,托管
14、C+比C#更优越的一点是可以在托管C+代码中调用非托管C +类,而不必采用COM交互功能。如果在托管类型上试图使用.NET不支持的特性(例如,模板或类的多继 加,编译器就会出现个错误。另外,在使用托管类时,还需要使用非标 准的C+特性(例如上述代码中的gc关键字)。因为C+允许低级指针操作,C+编译器不能生成可以通过CLR内存类 型安全测试的代码。如果CLR把代码标识为内存类型安全是非常重要的, 就需要用其他些语言编写源代码,例如C#或Visual Basic 2005o(3) Visual # 2005最新添加的语言是Visual 2005在.NET Framework 1.1版本推出之前,
15、 用户必须下载相应的软件,才能使用J#。现在J#语言内置于.NET Frame work中。因此,J#用户可以利用Visual Studio 2005的所有常见特性。Mier osoft希望大多数J+用户认为他们在使用.NET时,将很容易使用J#。J# 不使用Java运行库,而是使用与其他.NET兼容语言样的基类库。这说 明,与C#和Visual Basic 2005 样,可以使用J#创建ASP.NET Web应用 程序、Windows窗体、XML Web服务和其他应用程序。(4)脚本语言脚本语言仍在使用之中,但由于.NET的推出,一般认为它们的重要性在 降低。另方面,JScript升级到JS
16、cript.NET。现在ASP.NET页面可以用J Script.NET编写,可以把JScript.NET当作一种编译语言来运行,而不是解 释性的语言,也可以编写强类型化的JScript.NET代码。有了 ASRNET后, 就没有必要在服务器端的Web页面上使用脚本语言了,但VBA仍用作Of fice文档和Visual Studio宏语言。(5) COM 和 COM+从技术上讲,COM和COM+并不是面向.NET的技术,因为基于它们的 组件不能编译为IL(但如果原来的COM组件是用C+编写的,使用托管C +,在某种程度上可以这么做)。但是,COM+仍然是个重要的工具, 因为其特性没有在.NET
17、中完全实现。另外,COM组件仍可以使用-.NET组合了 COM的互操作性,从而使托管代码可以调用COM组件,COM 组件也可以调用托管代码(见第33章)。在一般情况下,把新组件编写为.N ET组件,其多数目的是比较方便,因为这样可以利用.NET基类和托管代 码的其他优点。1.3详细介绍中间语言通过前面的学习,我们理解了 Microsoft中间语言显然在.NET Framework 中有非常重要的作用。C#开发人员应明白,C#代码在执行前要编译为中 间语言(实际上,C#编译器仅编译为托管代码),这是有意义的,现在应详 细讨论下IL的主要特征,因为面向.NET的所有语言在逻辑上都需要支 持的主要特
18、征。下面就是中间语言的主要特征: 面向对象和使用接口 值类型和引用类型之间的巨大差别 强数据类型 使用异常来处理错误 使用特性(attribute)下面详细讨论这些特征。1.3.1 面向对象和接口的支持.NET的语言无关性还有一些实际的限制。中间语言在设计时就打算实现 某些特殊的编程方法,这表示面向它的语言必须与编程方法兼容,Micros 。为IL选择的特定道路是传统的面向对象的编程,带有类的单继承性。注意:不熟悉面向对象功能概念的读者应参考附录A,获得更多的信息。附 袤可以从www.wrox. com上下载。除了传统的面向对象编程外,中间语言还引入了接口的概念,它们显示了 在带有COM的Wi
19、ndows下的第一个实现方式。.NET接口与COM接口不 同,它们不需要支持任何COM基础结构,例如,它们不是派生自lUnkno wn,也没有对应的GUID。但它们与COM接口共享下述理念:提供个 契约,实现给定接口的类必须提供该接口指定的方法和属性的实现方式。前面介绍了使用.NET意味着要编译为中间语言,即需要使用传统的面向 对象的方法来编程。但这并不能提供语言的互操作性。毕竟,C+和Java 都使用相同的面向对象的范型,但它们仍不是可交互操作的语言。下面需 要详细探讨一下语言互操作性的概念。首先,需要确定一下语言互操作性的含义。毕竟,COM允许以不同语言 编写的组件一起工作,即可以调用彼此
20、的方法。这就足够了吗? COM是 个二进制标准,允许组件实例化其他组件,调用它们的方法或属性,而无需考虑编写相关组件的语言。但为了实现这个功能,每个对象都必须通 过COM运行库来实例化,通过接口来访问。根据相关组件的线程模型, 不同线程上内存空间和运行组件之间要编组数据,这还可能造成很大的性 能损失。在极端情况下,组件保存为可执行文件,而不是DLL文件,还必 须创建单独的进程来运行它们。重要的是组件要能与其他组件通信,但仅 通过COM运行库进行通信。无论COM是用于允许使用不同语言的组件 直接彼此通信,或者创建彼此的实例,系统都把COM作为中间件来处理。 不仅如此,COM结构还不允许利用继承实
21、现,即它丧失了面向对象编程 的许多优势。个相关的问题是,在调试时,仍必须单独调试用不同语言编写的组件。 这样就不可能在调试器上调试不同语言的代码To语言互操作性的真正含 义是用种语言编写的类应能直接与用另一种语言编写的类通信。特别 是: 用种语言编写的类应能继承用另种语言编写的类。 个类应能包含另个类的实例,而不管它们是使用什么语言编写的。 个对象应能直接调用用其他语言编写的另个对象的方法。 对象(或对象的引用)应能在方法之间传递。 在不同的语言之间调用方法时,应能在调试器中调试这些方法调用,即调试不同语言编写的源代码。这是个雄心勃勃的目标,但令人惊讶的是,.NET和中间语言已经实现 了这个目
22、标。在调试器上调试方法时,Visual Studio 2005 IDE提供了这样 的工具(不是CLR提供的)。1.3.2 相异值类型和引用类型与其他编程语言样,中间语言提供了许多预定义的基本数据类型。它的 个特性是值类型和引用类型有明显的区别。对于值类型,变量直接保存 其数据,而对于引用类型,变量仅保存地址,对应的数据可以在该地址中 找到。在C+中,引用类型类似于通过指针来访问变量,而在Visual Basic中, 与引用类型最相似的是对象,Visual Basic 6总是通过引用来访问对象。中间 语言也有数据存储的规范:引用类型的实例总是存储在一个名为托管堆的 内存区域中,值类型一般存储在堆
23、栈中(但如果值类型在引用类型中声明为 字段,它们就内联存储在堆中)。第2章“C#基础”讨论堆栈和堆,及其 工作原理。1.3.3 强数据类型中间语言的个重要方面是它基于强数据类型。所有的变量都清晰地标记 为属于某个特定数据类型(在中间语言中没有Visual Basic和脚本语言的 riant数据类型)。特别是中间语言一般不允许对模糊的数据类型执行任何操 作。例如,Visual Basic 6开发人员习惯于传递变量,而无需考虑它们的类型, 因为Visual Basic6会自动进行所需的类型转换。C+开发人员习惯于在不 同类型之间转换指针类型。执行这类操作将大大提高性能,但破坏了类型 的安全性。因此
24、,这类操作只能在某些编译为托管代码的语言中的特殊情 况下进行。确实,指针(相对于引用)只能在标记了的C#代码块中使用,但 在Visual Basic中不能使用(但一般在托管C+中允许使用)。在代码中使用 指针会立即导致CLR提供的内存类型安全性检查失败。注意,些与.NET兼容的语言,例如Visual Basic 2005,在类型化方面上 要求仍比较松,但这是可以的,因为编译器在后台确保在生成的IL上强制 类型安全。尽管强迫实现类型的安全性最初会降低性能,但在许多情况下,我们从.N ET提供的、依赖于类型安全的服务中获得的好处更多。这些服务包括: 语言的互操作性 垃圾收集 安全性 应用程序域下面
25、讨论强数据类型化对这些.NET特性非常重要的原因。L语言互操作性中强数据类型的重要性如果类派生其他类,或包含其他类的实例,它就需要知道其他类使用的所 有数据类型,这就是强数据类型非常重要的原因。实际上,过去没有任何 系统指定这些信息,从而成为语言继承和交互操作的真正障碍。这类信息 不只是在个标准的可执行文件或DLL中出现。假定Visual Basic 2005类中的个方法被定义为返回个整型Visual Basic 2005可以使用的标准数据类型之一。但C#没有该名称的数据类型。显然,我们只能从该类中派生,再使用这个方法,如果编译器知道如何把 Visual Basic 2005的整型类型映射为C
26、#定义的某种已知类型,就可以在C #代码中使用返回的类型。这个问题在NET中是如何解决的?(1)通用类型系统(CTS)这个数据类型问题在.NET中使用通用类型系统(CTS)得到了解决。CTS定 义了可以在中间语言中使用的预定义数据类型,所有面向.NET Framewor k的语言都可以生成最终基于这些类型的编译代码。例如,Visual Basic 2005的整型实际上是个32位有符号的整数,它实际 映射为中间语言类型Int32。因此在中间语言代码中就指定这种数据类型。 C#编译器可以使用这种类型,所以就不会有问题了。在源代码中,C#用 关键字int来表示Int32,所以编译器就认为Visual
27、 Basic 2005方法返回 个int类型的值。通用类型系统不仅指定了基本数据类型,还定义了一个内容丰富的类型层 次结构,其中包含设计合理的位置,在这些位置上,代码允许定义它自己的类型。 的方法,图1-1这个树形结构中的类型说明如表1-I所示。表!-1类型含Type代表任何类型的基类Value Type代表任何值类型的基类Reference Types通过引用来访问,且存储在堆中的任何数据类型Built-in Value Types包含大多数标准基本类型,可以表示数字、 Boolean值或字符Enumerations枚举值的集合User-defined Value Types在源代码中定义,
28、且保存为值类型的数据类型。 在C#中,它表示结构Interface Types接口Pointer Types指针Self-describing Types为垃圾回收器提供对它们本身有益的信息的数 据类型(参见下一节)Arrays包含对象数组的类型Class Types可自我描述的类型,但不是数组Delegates用于把引用包含在方法中的类型User-definedReferenceTypes在源代码中定义,且保存为引用类型的数据类 型。在C#中,它表示类Boxed Value Types值类型,临时打包放在一个引用中,以便于存储 在堆中这里没有列出内置的所有值类型,因为第3章将详细介绍它们。在
29、C#中, 编译器识别的每个预定义类型都映射为一个IL内置类型。这与Visual Bas ic 2005是一样的。(2)公共语言规范(CLS)公共语言规范(Common Language Specification, CLS)和通用类型系统起 确保语言的互操作性。CLS是个最低标准集,所有面向.NET的编译器 都必须支持它。因为IL是种内涵非常丰富的语言,大多数编译器的编写 人员有可能把给定编译器的功能限制为只支持IL和CLS提供的一部分特 性。只要编译器支持已在CLS中定义的内容,这就是很不错的。提示:编写非CLS兼容代码应该是完全可以接受的,只是编写了这种代码后, 就不能保证编译好的IL代码
30、完全支持语言的互操作性。下面的一个例子是有关区分大小写字母的。IL是区分大小写的语言。使用 这些语言的开发人员常常利用区分大小写所提供的灵活性来选择变量名。 但Visual Basic 2005是不区分大小写的语言。CLS就要指定CLS兼容代码 不使用任何只根据大小写来区分的名称。因此,Visual Basic 2005代码可 以与CLS兼容代码起使用。这个例子说明了 CLS的两种工作方式。首先是各个编译器的功能不必强大 到支持.NET的所有功能,这将鼓励人们为其他面向.NET的编程语言开发 编译器。第二,它梃供如下保证:如果限制类只能使用CLS兼容的特性, 就要保证用其他语言编写的代码可以使
31、用这个类。这种方法的优点是使用CLS兼容特性的限制只适用于公共和受保护的类成 员和公共类。在类的私有实现方式中,可以编写非CLS代码,因为其他程 序集(托管代码的单元,参见本章后面的内容)中的代码不能访问这部分代 码。这里不深入讨论CLS规范。在一般情况下,CLS对C#代码的影响不会太 ,因为C#中的非CLS兼容特性非常少。2 .垃圾收集垃圾收集器用来在.NET中进行内存管理,特别是它可以恢复正在运行中 的应用程序需要的内存。到目前为止,Windows平台已经使用了两种技术 来释放进程向系统动态请求的内存:完全以手工方式使应用程序代码完成这些工作。让对象维护引用计数。让应用程序代码负责释放内存
32、是低级、高性能的语言使用的技术,例如C +。这种技术很有效,且可以让资源在不需要时就释放(一般情况下),但 其最大的缺点是频繁出现错误。请求内存的代码还必须明确通知系统它什 么时候不再需要该内存。但这是很容易被遗漏的,从而导致内存泄漏。尽管现代的开发环境提供了帮助检测内存泄漏的工具,但它们很难跟踪错 误,因为直到内存已大量泄漏从而使Windows拒绝为进程提供资源时,它们会发挥作用。到那个时候,由于对内存的需求,会使整个计算机变得 相当慢。维护引用计数是COM对象采用的一种技术,其方法是每个COM组件都 保留一个计数,记录客户机目前对它的引用数。当这个计数下降到时, 组件就会删除自己,并释放相
33、应的内存和资源。它带来的问题是仍需要客 户机通知组件它们已经完成了内存的使用。只要有一个客户机没有这么 做,对象就仍驻留在内存中。在某些方面,这是比C+内存泄漏更为严重 的问题,因为COM对象可能存在于它自己的进程中,从来不会被系统删 除(在C+内存泄漏问题上,系统至少可以在进程中断时释放所有的内存)。.NET运行库采用的方法是垃圾收集器,这是个程序,其目的是清理内 存,方法是所有动态请求的内存都分配到堆上(这对所有的语言都一样,但 在.NET中,CLR维护它自己的托管堆,以供NET应用程序使用),当NE T检测到给定进程的托管堆已满,需要清理时,就调用垃圾收集器。垃圾 收集器处理目前代码中的
34、所有变量,检查对存储在托管堆上的对象的引 用,确定哪些对象可以从代码中访问即哪些对象有引用。没有引用的 对象就不能再从代码中访问,因而被删除。Java就使用与此类似的垃圾收 集系统。之所以在.NET中使用垃圾收集器,是因为中间语言已用来处理进程。其 规则要求,第一,不能引用已有的对象,除非复制已有的引用。第二,中 间语言是类型安全的语言。在这里,其含义是如果存在对对象的任何引用, 该引用中就有足够的信息来确定对象的类型。垃圾收集器机制不能和诸如非托管C+这样的语言起使用,因为C+ 允许指针自由地转换数据类型。垃圾收集器的个重要方面是它是不确定的。换言之,不能保证什么时候 会调用垃圾收集器:.N
35、ET运行库决定需要它时,就可以调用它(除非明确 调用垃圾收集器)。但可以重写这个过程,在代码中调用垃圾收集器。3 .安全性.NET很好地补足7 Windows提供的安全机制,因为它提供的安全机制是 基于代码的安全性,而Windows仅提供了基于角色的安全性。基于角色的安全性建立在运行进程的账户的身份基础上,换言之,就是谁 拥有和运行进程。另一方面,基于代码的安全性建立在代码实际执行的任 务和代码的可信程度上。由于中间语言提供了强大的类型安全性,所以C LR可以在运行代码前检查它,以确定是否有需要的安全权限。.NET还提 供了一种机制,可以在运行代码前指定代码需要什么安全权限。基于代码的安全性非
36、常重要,原因是它降低了运行怀疑其出处的代码的风 险(例如代码是从Internet上下载来的)。即使代码运行在管理员账户下,也 有可能使用基于代码的安全性,来确定这段代码是否仍不能执行管理员账 户一般允许执行的某些类型的操作,例如读写环境变量、读写注册表或访 问.NET反射特性。安全问题详见本书后面的第16章。4 .应用程序域应用程序域是.NET中的一个重要技术改进,它用于减少运行应用程序的 系统开销,这些应用程序需要与其他程序分离开来,但同时还需要彼此通 信。典型的例子是Web服务器应用程序,它需要同时响应许多浏览器请求。 因此,要有许多组件实例同时响应这些同时运行的请求。在.NET没有开发出
37、来前,可以让这些实例共享同一个进程,但此时个1骁 外隔开来。这本J,每平迴行枷畝蒯就有可能导致整个网站的崩溃;也可以把这些实例孤立在不同 釘进程中,但返样做件最飒关性能的系统开销。方式是通过进程来实现的。在运行一个新的 环境内运行。Windows通过地址空间把进程 GB的虚拟内存来存储其数据和可执行代码(,GB对应于况 位系先/3番统要用更多的内存)。Windows利用额外的 接玄齬玲虚拟遗a2到物理内存或磁盘空间的个特殊区域中,塁事会1冲奶,CDM虚拟地址空间块映射的物理内存之间不能有J .卷,送料情如图2,图1-2在一般情况下,任何进程都只能通过指定虚拟内存中的一个地址来访问内 存即进程不
38、能直接访问物理内存,因此个进程不可能访问分配绐另 个进程的内存。这样就可以确保任何执行出错的代码不会损害其地址空 间以外的数据(注意在Windows 95/98上,这些保护措施不像在Windows NT/2000/XP/2003上那样强大,所以理论上存在应用程序因写入不对应的 内存而导致Windows崩溃的可能性)。巡程,4GB应做“再底网程月, 个残闲程井使用-* 虐船四年进程不仅是运行代码的实例相互隔离的种方式,在Windows NT/2000/ XP/2003系统上,它们还可以构成分配了安全权限和许可的单元。每个进 程都有自己的安全标识,明确地表示Windows允许该进程可以执行的操作。
39、 进程对确保安全有很大的帮助,而它们的一大缺点是性能。许多进程常常 在起工作,因此需要相互通信。个常见的例子是进程调用个COM 组件,而该COM组件是可执行的,因此需要在它自己的进程上运行。在 COM中使用代理时也会发生类似的情况。因为进程不能共享任何内存, 所以必须使用个复杂的编组过程在进程之间复制数据。这对性能有非常 ,使组件一盘工作,但不希望性能有损失,惟一的方法 且件,让所有的组件在同一个地址空间中运行其 件会影响其他组件。皮雨程序国, 个/5用软井健用一安慮 期内存件的一种方式,它不会导致因在进程之间传送数据而 .方法是把任何一个进程分解到多个应用程序域中,每 、应个应用程序,执行的
40、每个线程都运行在个具体 ,图!-3所示。图1-3如果不同的可执行文件都运行在同一个进程空间中,显然它们就能轻松地 共享数据,因为理论上它们可以直接访问彼此的数据。虽然在理论上这是 可以实现的,但是CLR会检查每个正在运行的应用程序的代码,以确保这 些代码不偏离它自己的数据区域,保证不发生直接访问其他进程的数据的 情况。这初看起来是不可能的,如何告诉程序要做什么工作,而又不真正 运行它?实际上,这么做通常是可能的,因为中间语言拥有强大的类型安全功能。 在大多数情况下,除非代码明确使用不安全的特性,例如指针,否则它使 用的数据类型可以确保内存不会被错误地访问。例如,.NET数组类型执 行边界检查,
41、以禁止执行超出边界的数组操作。如果运行的应用程序的确 需要与运行在不同应用程序域中的其他应用程序通信或共享数据,就必须 调用.NET的远程服务。被验证不能访问超出其应用程序域的数据(而不是通过明确的远程机制)的 代码就是内存类型安全的代码,这种代码与运行在同一个进程中但应用程 序域不同的类型安全代码起运行是安全的。1.1.4 通过异常方法处理错误.NET Framework可以根据异常使用相同的机制处理错误情况,这与Java 和C+是样的。C+开发人员应注意至,由于IL有非常强大的类型系 统,所以在IL中以C+的方式使用异常不会带来相关的性能问题。另外,. NET和C#也支持finally块,
42、这是许多C+开发人员长久以来的愿望。第12章会详细讨论异常。简要地说,代码的某些领域被看作是异常处理程 序例程,每个例程都能处理某种特殊的错误情况(例如,找不到文件,或拒 绝执行某些操作的许可)。这些条件可以定义得很宽或很窄。异常结构确保 在发生错误情况时,执行进程立即跳到异常处理程序例程上,处理错误情 况。异常处理的结构还提供了一种方便的方式,当对象包含错误情况的准确信 息时,该对象就可以传送给错误处理例程。这个对象包括给用户提供的相 应信息和在代码的什么地方检测到错误的确切信息。大多数异常处理结构,包括异常发生时的程序流控制,都是由高级语言处 理的,例如C#、Visual Basic 20
43、05和C+,任何中间语言命令都不支持它。 例如,C#使用try卜catch仆和finaUy仆代码块来处理它,详见第12章。.NET提供了一种基础结构,让面向.NET的编译器支持异常处理。特别是 它提供了一组.NET类来表示异常,语言的互操作性则允许错误处理代码 处理被抛出的异常对象,无论错误处理代码使用什么语言编写,都是这样。 语言的无关性没有体现在C+和Java的异常处理中,但在COM的错误处 理机制中有一定限度的体现。COM的错误处理机制包括从方法中返回错 误代码以及传递错误对象。在不同的语言中,异常的处理是一致的,这是 多语言开发的重要一环。1.1.5 特性的使用特性(attribute
44、)是使用C+编写COM组件的开发人员很熟悉的个功能(使 用 Microsoft 的 COM 接口定义语言(Interface Definition Language, IDL)O 特性最初是为了在程序中提供与某些项相关的额外信息,以供编译器使 用。.NET支持特性,因此现在C+、C#和Visual Basic 2005也支持特性。但 在.NET中,对特性的革新是建立了一个机制,通过该机制可以在源代码 中定义自己的特性。这些用户定义的特性将和对应数据类型或方法的元数 据放在起,这对于文档说明书十分有用,它们和反射技术起使用,以 根据特性执行编程任务。另外,与.NET的语言无关性的基本原理一样,
45、特性也可以在种语言的源代码中定义,而被用另种语言编写的代码读 取。本书的第11章详细介绍了特性。1.4 程序集程序集(assembly)是包含编译好的、面向.NET Framework的代码的逻辑单 元。本章不详细论述程序集,而在第15章中论述,下面概述其中的要点。 程序集是完全自我描述性的,也是一个逻辑单元而不是物理单元,它可以 存储在多个文件中(动态程序集的确存储在内存中,而不是存储在文件中)。 如果个程序集存储在多个文件中,其中就会有一个包含入口点的主文 件,该文件描述了程序集中的其他文件。注意可执行代码和库代码使用相同的程序集结构。惟的区别是可执行的 程序集包含一个主程序入口点,而库程
46、序集则不包含。程序集的一个重要特性是它们包含的元数据描述了对应代码中定义的类 型和方法。程序集也包含描述程序集本身的元数据,这种程序集元数据包 含在个称为程序集清单的区域中,可以检查程序集的版本及其完整性。注意:ildasm是个基于Windows的实用程序,可以用于检查程序集的内容, 包括程序集清单和元数据。第15章将介绍ildasm。程序集包含程序的元数据,表示调用给定程序集中的代码的应用程序或其 他程序集不需要指定注册表或其他数据源,以便确定如何使用该程序集。 这与以前的COM有很大的不同,以前,组件的GUID和接口必须从注册 表中获取,在某些情况下,方法和属性的详细信息也需要从类型库中读
47、取。 把数据分散在3个以上的不同位置上,可能会出现信息不同步的情况,从 而妨碍其他软件成功地使用该组件。有了程序集后,就不会发生这种情况, 因为所有的元数据都与程序的可执行指令存储在起。注意,即使程序集 存储在几个文件中,数据也不会出现不同步的问题。这是因为包含程序集 入口的文件也存储了其他文件的细节、散列和内容,如果个文件被替换, 或者被塞满,系统肯定会检测出来,并拒绝加载程序集。程序集有两种类型:共享程序集和私有程序集。1.4.1 私有程序集私有程序集是最简单的种程序集类型。私有程序集一般附带在某些软件 上,且只能用于该软件中。附带私有程序集的常见情况是,以可执行文件 或许多库的方式提供应用程序,这些库包含的代码只能用于该应用程序。系统可以保证私有程序集不被其他软件使用,因为应用程序只能加载位于 主执行文件所在文件夹或其子文件夹中的程序集。用户一般会希望把商用软件安装在它自己的目录下,这样软件包没有覆 盖、修改或加载另个软件包的私有程序集的风险。私有程序集只能用于 自己的软件包,这样,用户对什么软件使用它们就有了更多的控制。因此, 不需要采取安全措施,因为这没有其他商用软件用某个新版本的程序集覆 盖原来的私有程序集的风险(但软件是专门执行怀有恶意的损害性操作的 情况除外)。名称也不会有冲突。如果私有程序集