分类

链接

2011 年 12 月
 1234
567891011
12131415161718
19202122232425
262728293031  

近期文章

热门标签

新人福利,免费薅羊毛

现在位置:    首页 > .NET > 正文
共享办公室出租
深入.net实质讨论系列(六)
.NET 暂无评论 阅读(1,829)

第Ⅰ部分 C# 语 言
类型

引用类型 接口类型

值类型

指针类型 自我描述类型

内置值类型 用户定义

的值类型

枚举 数组

类类型
委托 装箱的值类型

用户定义的

引用类型

图 1-1

这个树形结构中的类型说明如表1-1所示。

表 1-1

类 型 含 义

Type 代表任何类型的基类

Value Type 代表任何值类型的基类

Reference Types 通过引用来访问,且存储在堆中的任何数据类型

Built-in Value Types 包含大多数标准基本类型,可以表示数字、Boolean值或字符

Enumerations 枚举值的集合

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

第1章 .NET体系结构的每个预定义类型都映射为一个IL内置类型。这与Visual Basic 2005是一样的。

(2) 公共语言规范(CLS)

公共语言规范(Common Language Specification,CLS)和通用类型系统一起确保语言的互操作性。CLS是一个最低标准集,所有面向.NET的编译器都必须支持它。因为IL是一种内涵非常丰富的语言,大多数编译器的编写人员有可能把给定编译器的功能限制为只支持IL和CLS提供的一部分特性。只要编译器支持已在CLS中定义的内容,这就是很不错的。

提示:

编写非CLS兼容代码应该是完全可以接受的,只是在编写了这种代码后,就不能保证编译好的IL代码完全支持语言的互操作性。

下面的一个例子是有关区分大小写字母的。IL是区分大小写的语言。使用这些语言的开发人员常常利用区分大小写所提供的灵活性来选择变量名。但Visual Basic 2005是不区分大小写的语言。CLS就要指定CLS兼容代码不使用任何只根据大小写来区分的名称。因此,Visual Basic 2005代码可以与CLS兼容代码一起使用。

这个例子说明了 CLS 的两种工作方式。首先是各个编译器的功能不必强大到支持.NET 的所有功能,这将鼓励人们为其他面向.NET的编程语言开发编译器。第二,它提供如下保证:如果限制类只能使用CLS兼容的特性,就要保证用其他语言编写的代码可以使用这个类。

这种方法的优点是使用 CLS 兼容特性的限制只适用于公共和受保护的类成员和公共类。在类的私有实现方式中,可以编写非CLS代码,因为其他程序集(托管代码的单元,参见本章后面的内容)中的代码不能访问这部分代码。

这里不深入讨论CLS规范。在一般情况下,CLS对C#代码的影响不会太大,因为C#中的非CLS兼容特性非常少。

2. 垃圾收集

垃圾收集器用来在.NET中进行内存管理,特别是它可以恢复正在运行中的应用程序需要的内存。到目前为止,Windows平台已经使用了两种技术来释放进程向系统动态请求的内存:

● 完全以手工方式使应用程序代码完成这些工作。

● 让对象维护引用计数。

让应用程序代码负责释放内存是低级高性能的语言使用的技术,例如C++。这种技术很有效,且可以让资源在不需要时就释放(一般情况下),但其最大的缺点是频繁出现错误。请求内存的代码还必须显式通知系统它什么时候不再需要该内存。但这是很容易被遗漏的,从而导致内存泄漏。

尽管现代的开发环境提供了帮助检测内存泄漏的工具,但它们很难跟踪错误,因为直到内存已大量泄漏从而使Windows拒绝为进程提供资源时,它们才会发挥作用。到那个时候,由于对内存的需求很大,会使整个计算机变得相当慢。

维护引用计数是COM对象采用的一种技术,其方法是每个COM组件都保留一个计

11 第Ⅰ部分 C# 语 言

数,记录客户机目前对它的引用数。当这个计数下降到0时,组件就会删除自己,并释放相应的内存和资源。它带来的问题是仍需要客户机通知组件它们已经完成了内存的使用。只要有一个客户机没有这么做,对象就仍驻留在内存中。在某些方面,这是比C++内存泄漏更为严重的问题,因为COM对象可能存在于它自己的进程中,从来不会被系统删除(在C++内存泄漏问题上,系统至少可以在进程中断时释放所有的内存)。

.NET运行库采用的方法是垃圾收集器,这是一个程序,其目的是清理内存,方法是所有动态请求的内存都分配到堆上(所有的语言都是这样处理的,但在.NET中,CLR维护它自己的托管堆,以供.NET应用程序使用),当.NET检测到给定进程的托管堆已满,需要清理时,就调用垃圾收集器。垃圾收集器处理目前代码中的所有变量,检查对存储在托管堆上的对象的引用,确定哪些对象可以从代码中访问—— 即哪些对象有引用。没有引用的对象就不能再从代码中访问,因而被删除。Java就使用与此类似的垃圾收集系统。

之所以在.NET中使用垃圾收集器,是因为中间语言已用来处理进程。其规则要求,第一,不能引用已有的对象,除非复制已有的引用。第二,中间语言是类型安全的语言。在这里,其含义是如果存在对对象的任何引用,该引用中就有足够的信息来确定对象的类型。

垃圾收集器机制不能和诸如非托管C++这样的语言一起使用,因为C++允许指针自由地转换数据类型。

垃圾收集器的一个重要方面是它的不确定性。换言之,不能保证什么时候会调用垃圾收集器:.NET运行库决定需要它时,就可以调用它(除非明确调用垃圾收集器)。但可以重写这个过程,在代码中调用垃圾收集器。

3. 安全性

.NET很好地补足了Windows提供的安全机制,因为它提供的安全机制是基于代码的安全性,而Windows仅提供了基于角色的安全性。

基于角色的安全性建立在运行进程的账户的身份基础上,换言之,就是谁拥有和运行进程。另一方面,基于代码的安全性建立在代码实际执行的任务和代码的可信程度上。由于中间语言提供了强大的类型安全性,所以CLR可以在运行代码前检查它,以确定是否有需要的安全权限。.NET还提供了一种机制,可以在运行代码前指定代码需要什么安全权限。

基于代码的安全性非常重要,原因是它降低了运行来历不明的代码的风险(例如代码是从Internet上下载来的)。即使代码运行在管理员账户下,也有可能使用基于代码的安全性,来确定这段代码是否仍不能执行管理员账户一般允许执行的某些类型的操作,例如读写环境变量、读写注册表或访问.NET反射特性。

安全问题详见本书后面的第19章。

4. 应用程序域

应用程序域是.NET中的一个重要技术改进,它用于减少运行应用程序的系统开销,这些应用程序需要与其他程序分离开来,但仍需要彼此通信。典型的例子是Web服务器应用程序,它需要同时响应许多浏览器请求。因此,要有许多组件实例同时响应这些同时运行的请求。

在.NET开发出来以前,可以让这些实例共享同一个进程,但此时一个运行的实例就有12

第1章 .NET体系结构可能导致整个网站的崩溃;也可以把这些实例孤立在不同的进程中,但这样做会增加相关性能的系统开销。

到现在为止,孤立代码的唯一方式是通过进程来实现的。在运行一个新的应用程序时,它会在一个进程环境内运行。Windows通过地址空间把进程分隔开来。这样,每个进程有4GB的虚拟内存来存储其数据和可执行代码(4GB对应于32位系统,64位系统要用更多的内存)。Windows利用额外的间接方式把这些虚拟内存映射到物理内存或磁盘空间的一个特殊区域中,每个进程都会有不同的映射,虚拟地址空间块映射的物理内存之间不能有重叠,这种情况如图1-2所示。

物理内存

进程1

物理内存或 虚拟

磁盘空间 4GB

内存
进程2

物理内存或

磁盘空间 4GB虚拟

内存

图 1-2

在一般情况下,任何进程都只能通过指定虚拟内存中的一个地址来访问内存——即进程不能直接访问物理内存,因此一个进程不可能访问分配给另一个进程的内存。这样就可以确保任何执行出错的代码不会损害其地址空间以外的数据(注意在Windows 95/98上,这些保护措施不像在Windows NT/2000/XP/2003上那样强大,所以理论上存在应用程序因写入不对应的内存而导致Windows崩溃的可能性)。

进程不仅是运行代码的实例相互隔离的一种方式,在Windows NT/2000/XP/2003系统上,它们还可以构成分配了安全权限和许可的单元。每个进程都有自己的安全标识,明确地表示Windows允许该进程可以执行的操作。

进程对确保安全有很大的帮助,而它们的一大缺点是性能。许多进程常常在一起工作,因此需要相互通信。一个常见的例子是进程调用一个COM组件,而该COM组件是可执行的,因此需要在它自己的进程上运行。在COM中使用代理时也会发生类似的情况。因为进程不能共享任何内存,所以必须使用一个复杂的编组过程在进程之间复制数据。这对性能有非常大的影响。如果需要使组件一起工作,但不希望性能有损失,唯一的方法是使用基于DLL的组件,让所有的组件在同一个地址空间中运行—— 其风险是执行出错的组件会影响其他组件。

应用程序域是分离组件的一种方式,它不会导致因在进程之间传送数据而产生的性能问题。其方法是把任何一个进程分解到多个应用程序域中,每个应用程序域大致对应一个应用程序,执行的每个线程都运行在一个具体的应用程序域中,如图1-3所示。

 

============ 欢迎各位老板打赏~ ===========

本文版权归Bruce's Blog所有,转载引用请完整注明以下信息:
本文作者:Bruce
本文地址:深入.net实质讨论系列(六) | Bruce's Blog

发表评论

留言无头像?