Wrox的C#高级编程第三版

Wrox的C#高级编程第三版第一部分第一章(13~15页)

http://blog.csdn.net/coofucoo/archive/2004/10/26/152488.aspxopen in new window

公共语言规范

Common Language Specification (CLS)与CTS一起以保证语言互操作性的实现。CLS是一个最小的标准集合,所有以.NET作为目标运行平台的编译器都必须遵守。因为IL是一个内涵丰富的语言,许多编译器的作者都喜欢实现IL 和 CTS所提供的功能限制的一个子集。这样也好,只要编译器支持CLS所支持的所有定义就好。

提示:你完全可以编写与CLS不兼容的代码。但是,如果你这样做,编译出来的IL代码就无法保证完全符合语言互操作性。

比如,让我们来看一个关于字母大小写的例子。IL是区分大小写的。用区分大小写的语言来编写程序的开发人员习惯利用这种区分大小写的灵活性来给他们的变量命名。但是,Visual Basic .NET却是不区分大小写的。CLS的工作就是使CLS兼容代码明白,他们不能使用任何两个仅仅在大小写上有所区别的名称。这样,Visual Basic .NET就可以和任何CLS兼容代码协同工作了。

这个例子说明了CLS有两种工作方式。首先,各个编译器功能不必非要强大到满足.NET的所有特性,这就鼓励了其他的编译器厂商将自己的语言加入到.NET这个大家庭来。第二,他也提供了这样的一个保证:如果你限制你的类使他满足CLS的兼容特性,那么其他的兼容语言编写的代码都可以使用你的类。

这种方法的优点是使用CLS兼容特性的限制只适用于公共类和类的公有或者受保护成员。而对于那些你的类中的私有成员的实现,你完全可以编写任何非CLS兼容代码,因为其他的程序集的代码是无论如何也无法访问这部分的。

在这里我们不需要详细的讨论CLS规范的细节。一般而言,因为C#中的非CLS兼容特性很少,所以CLS对你的C#代码影响不大。

1.3.3.2 垃圾收集

垃圾收集器是.NET的内存管理方法,特别是针对如何回收运行的应用程序申请的内存的这个问题,垃圾收集器提供了一个好地解决办法。至今为止,针对如何释放进程动态申请的系统内存这个问题,在Windows平台上已经出现了两种技术。

❑ 让应用程序代码自己手工实现内存释放
❑ 让对象为呼应用数目

让应用程序代码来负责释放内存是低级,但是高性能的语言采用的方法,比如C++。这种方法是高效的,他的优点是资源一旦不再需要就立即释放(一般情况下)。但是,他也带来了很大的缺点,由此而引发缺陷非常频繁。代码申请的内存当他们不再需要时当然应该明确的告知系统。但是,非常容易就忽略这一点,从来导致了内存泄漏。

尽管现代开发环境确实提供了帮助探测内存泄漏的工具,但是因为此类问题只有到大量的内存泄漏以至于Windows拒绝批准进程申请内存时才会被发现,所以仍然有难以捕捉到的bug。从这一点上来说,由于对内存的需求,整个计算机就会逐渐的变的相当慢。

COM采用维护应用计数的方法来解决这个问题。COM的方法是每个COM组件都维护一个计数器,这个计数器保存的是当前有多少客户机应用自己。当这个计数器的数值下降到零时,组件就会销毁自己并且释放相关的内存和资源。但是问题时COM的这种解决办法仍然是依靠客户机及时地通知组件他们已经使用引用完毕。只要有一个客户机没有及时销毁自己,那么组件就会留在内存中。但是从某种程度上讲,这种内存泄漏或许比C++的内存泄漏更为严重,因为COM对象会保存在自己的进程中而永远不会被系统删除(至少对于C++内存泄漏问题,系统可以在进程停止时重新收回所有的内存资源)。

现在,.NET运行时改成依靠垃圾收集器来回收资源了。垃圾收集器就是一个专门用来整理内存的程序。方法是将所有动态申请的内存都分配到堆上(这对所有的语言都一样,但是在.NET中,CLR要单独维护自己管理的堆以供.NET应用程序使用)。情况常常是这样的,当.NET检测到分配给一个进程的托管堆已经变满因而需要整理时,他就会调用垃圾收集器。垃圾收集器就会扫描你的代码中的变量,检查托管堆上的对象的引用,以确定哪些个是现在还在使用的——也就是说哪些对象还有对自己的引用。任何已经没有你的代码引用的对象都会被认为是不再需要使用的,因此他们都将被移出。Java也有一个类似的垃圾收集器。

.NET中的垃圾收集器本身就是为了适应进程的工作而设计的。其原则是:除非你复制一个已经存在的引用,否则你不可以为已经存在的对象建立引用,并且IL是类型安全的。这里的意思就是说,如果存在一个对对象的引用,那么我们就有足够的信息以明确的确定对象的类型。

垃圾收集机制就不适合用在象非托管C++这样语言上,例如,C++允许指针在不同的类型中自由的转换。

垃圾收集机制的一个重要的特点是它的操作时间是不确定的。换句话说,你不能保证垃圾收集器是何时被调用的,当CLR觉得有必要调用它的时候他就会被调用(除非你明确调用它)。尽管你也可以不考虑这些过程而直接在你的代码中调用垃圾收集器。

1.3.3.3 安全性

因为.NET能够提供基于代码的安全性机制,所以.NET在安全性机制方面是优于Windows的,更何况Windows仅仅提供了基于角色的安全性。

基于角色的安全性机制是基于识别进程所运行的帐户的基础上的,换句话说,是谁拥有并运行这个进程?另一方面,寄予代码的安全性是建立在识别代码本身是如何执行以及其本身的可信度有多大的基础上的。幸亏IL提供了强大的的类型安全机制,CLR才能够在代码运行之前就预先检查代码的安全性以确定需要的安全许可。.NET也提供了一种机制,代码可以预先指出它需要运行在什么样的安全许可下。

基于代码的安全性的重要性是她可以降低运行来源不明的代码所冒的风险(比如你在Internet所辖在德代码)。例如,即便是代码是运行在管理员帐户下,基于代码的安全性仍然可以指出这段代码仍然不应该被允许执行某些管理员通常应该被允许的操作,例如读或写环境变量,读或写注册表,或者访问.NET反射特性。

安全性的详细问题我们将在14章讨论。

1.3.3.4 应用程序域

应用程序域是.NET的一项重要改革。运行的应用程序既需要彼此隔离又需要可以彼此通讯,像这样一个棘手的问题,应用程序域就是设计来解决他的。这方面经典的案例是一个Web应用服务器同时需要相应许多的游览器的请求。因此,应用服务器可能就同时为了给不同的请求服务而运行许多的不同的实例。

在.NET出现之前,有两种方法可以解决这个问题。一个是允许这样实例共享同一个进程,但是这样可能导致出现一个实例失败就会使整个Web服务器关闭的现象。另一个方法是将这些实例彼此孤立在不同的进程中,但是这样也带来了性能的降低。

到目前为止,我们是通过将代码运行在不同的进程中来实现代码隔离的。当你启动一个应用程序的时候,他是在一个进程的环境中运行的。Windows系统通过地址空间来彼此隔离进程。具体的方法是每个进程都有4GB的虚拟内存用来存放他自己的数据和可执行代码(对32位系统来说是4GB,对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组件需要运行在自己进程中。在COM中使用代理也会有类似情况发生。由于进程之间是不共享内存的,所以一个复杂的工作就是在进程之间复制数据。这样做的结果就是带来了巨大的性能损失。如果你希望使组件协同工作而不希望有性能的损失,那你就必须使用基于DLL的组件技术,这样所有的代码都会运行在同样的地址空间里——当然,这样带来的风险就是,如果其中的一个组件运行出错,那么这个进程中所有的程序都会关闭。

设计应用程序域的目的就是要在对性能毫无损失的情况下解决进程间共享数据的问题。具体的做法是,每一个进程都被划分到一个应用程序域中。每个应用程序域大体上对应于隐格单独的应用程序,每个执行的线程都运行在一个特定的应用程序域中(如图1-3所示)。

+---------------------+
|  进程:4GB虚拟内存    |
+---------------------+
|  应用程序域:         |
|  一个应用程序使用    |
|  虚拟内存的一部分    |
+---------------------+
|                     |
+---------------------+
|  应用程序域:         |
|  另一个应用程序使用  |
|  虚拟内存的一部分    |
+---------------------+
|                     |
+---------------------+

图1-3

C#高级编程第三版第一部分第一章(16~18页)

如果不同的可执行代码在同样的进程空间里运行,那么显然他们可以方便的共享数据,因为理论上他们可以直接访问彼此的数据。尽管理论上这样是可行的,但是,在实际情况下CLR会检查每个运行的代码以保证这种情况不会发生,这样就保证了代码不会超出自己所拥有的区域执行操作了。乍一看这似乎是不可能的,毕竟如果你不去实际运行代码你又怎样知道代码究竟是怎样运行的?

实际上,因为IL拥有强大的类型安全机制,所以这样做是可能的。在大多数情况下,除非代码使用了不安全的特性,比如指针,否则数据类型就会保证内存不会被不正当的访问。比如,.NET数组类型会执行边界检查以确保任何越界数组操作都是不允许的。如果一个运行的应用程序确实需要与不同的应用程序域的另一个运行的应用程序通信或者共享数据,那么它就必须通过调用.NET的远程服务的方式实现。

这种验证代码以确保代码不可以超出应用程序域访问数据的机制就叫做内存类型安全(不同于通过外部远程调用机制)。这样处于一个进程中的不同应用程序域的类型安全代码就可以安全运行而互不干扰。

1.3.3.3 通过异常处理错误

与Java 和 C++所采用的异常处理机制一样,.NET Framework也设计为采用这种方便的基于异常的错误处理机制。C++开发者应该注意到,由于同样IL有强大的输入系统,所以在IL中以和C++同样的方式使用异常并不会带来性能损失。.NET 和 C#还支持许多C++开发者期待的finally块。

我们将要在第11章详细讨论异常。简单的说,异常处理的方法就是专门指定一块代码来作为异常处理例行程序负责处理特殊的异常情况,每一块代码都可以处理特定的错误状况(例如,一个文件没有找到,或者某些操作被拒绝执行)。这种特定的错误状况可以根据你的需要而定制,你可以定的条件很窄也可以很宽。异常处理体系结构可以确保一旦有错误情况发生,程序就会立即跳到异常处理例行程序中那个处理此特定情况的代码块处执行。

异常处理体系结构还提供了一个方便的办法,你可以传递一个包含明确的异常信息的对象给一个异常处理例行程序。这个对象可能包含用户的特定消息和具体是代码的什么地方检测到了异常。

当异常发生时,大多数异常处理体系结构,包括程序流控制,都是由高级语言处理的(例如C#, Visual Basic .NET, C++),而不被任何特定的IL命令所支持。例如,C#利用代码块try { }, catch{ }, 和 finally{ }来处理异常(详细情况请参看第11章)。

但是,.NET也确实提供了一个基础结构,让所有以.NET为目标运行平台的编译器支持异常处理。特别是,.NET提供了一个.NET类的集合用来专门表示异常,并且语言互操作性允许异常处理代码处理抛出的异常对象而不管处理异常的代码使用什么语言编写的。这种语言无关性是C++ 和 Java的异常处理机制都不能实现的,但是在COM的错误处理机制种有一定的体现,这包括从方法中返回错误代码以及传递错误对象。在不同的语言中保持异常处理的一致性对多语言开发来说是至关重要的一个环节。

1.3.3.4 属性的使用

用C++编写COM组件的开发者一定对属性这个特性很熟悉(通过他们使用Microsoft’s COM Interface Definition Language [IDL])。属性最初的用意是为了给编译器提供关于某些程序相关的额外信息使用。

属性是由.NET支持的,因此现在C++, C#, 和 Visual Basic .NET肯定也都支持。但是,.NET的属性有所创新,那就是它提供了一种机制,使你可以在你的源代码中定义你自己的属性。这些用户定义的属性将会被放置在数据类型或者方法所对应的元数据中。这些元数据可以用于文档的编制,他们可以和反射技术同时使用来实现基于属性的执行设计任务。另外,与.NET的语言无关性的原理一样,可以在源代码中用一种语言定义属性而用另一种语言将其读出。

我们将要在第10章讨论属性。

1.4 程序集

一个程序集是逻辑单元,它包含为.NET Framework所编译的代码。在这里我们不希望过多的关注程序集的细节,因为在第13章我们将要详细讨论,但是在这里概括他的要点。

程序集是完全自我描述的,而且是一个逻辑上的单元而不是物理上的,这也就表示他可以本存储在不只一个文件里(实际上动态程序集是存储在内存里的,而不是文件里)。如果一个程序集被存储在多个文件里,那么就会有一个主文件包含主要的入口点并且描述程序集中的其他文件。

应该注意到,对于可执行代码和库代码来说,他们的程序集的结构都是一样的。他们其中的唯一差别就是,可执行代码的程序集里包含一个主要程序出口点,而库代码的程序集里没有。

程序集的一个重要特性使它包含对应代码中定义的描述类型和方法元数据。但是,一个程序集也包含一些描述程序集自身元数据。这些包含在一个叫做程序集清单的程序集元数据,可以用来检查程序集的版本信息以及完整性。

注意:ildasm,一个基于Windows的使用工具,可以用来检查程序集的内容,包括程序集的清单和元数据。我们将在第13章讨论ildasm。

事实上程序集包含程序的元数据意味着应用程序或者其他的程序集调用别的代码时,有了程序集所描述的信息,就不需要为了如何使用程序集而查询注册表或者其他的数据源了。相对于老的COM的做法,现在的这种做法有重大的突破。在以前的老方法中,组件的GUIDs和借口必须从注册表中获得,而且在某些情况下,方法和属性的细节还需要从类型库中读取。

第一部分第一章(18~21页)

1.4.1 私有程序集

私有程序集是使程序集中最简单的类型。他们一班附带于软件上面并且也仅仅被该软件所用。一般情况下附带私有程序集的情况是这样的,你在编写一个由一个可执行文件和好几个类库组成的应用程序,而类库中的代码你只想让这个应用程序使用。

系统保证私有程序集不会被其他的软件所使用,因为应用程序只可以加载与主可执行文件在同一目录或者其子目录的私有程序集。

由于我们一般认为商业软件总是会被安装属于他自己的一个目录里,这就是说没有软件包被以外覆盖,修改或者应用程序以外加载其他软件包的程序集的可能。因此,程序集只能够被自己所对应的软件包使用,所以对于什么软件使用他们你就有更多的控制权利。因此,不需要采取安全防范措施,根本就不会出现如其他的商用软件用一些新版本覆盖你的程序集的可能(专门设计用来执行恶意破坏的软件除外)。当然,名称也没有任何冲突。如果你的私有程序集的类和其他的人的私有程序集的类有相同的名称也不要紧,因为任何一个应用程序都只能够使用自己的私有程序集。

因为一个私有程序集是完全自包含的,所以配置他们的过程是非常简单的。在部署是你只需要简单的将应用程序文件目录系统的特定目录中就可以了(不需要注册注册表项)。这个过程被称为“零影响安装“(或者xcopy安装,或者是我们说的绿色安装)。

1.4.2 共享程序集

共享程序集被规定为任何应用程序都可以使用的公共库。因为任何软件都可以访问共享程序集。需要采取一些防护措施来避免如下的风险:

❑ 名称冲突,其他的公司的共享程序集执行类型和你自己的共享程序集有相同的名称。由于客户机代码在理论上存在同时访问两个程序集的可能,所以这是一个严重的问题。
❑ 存在一个程序基本不同版本的同一个程序集重写的可能,并且新版本可能与某些已经存在的客户机代码不兼容。

解决这些问题的办法是将共享程序集放置在文件系统中一个特定的目录子树中,被称作global assembly cache (GAC)。和私有程序集不同,不可以简单的将共享程序集复制到应用程序的目录中,它需要被专门的安装到缓冲区中。这个过程可以利用许多.NET工具来完成,包括对程序集进行特定的检查,也包括在程序集缓冲区中建立一个小的目录层次结构来确保程序集的完整性。

为了消除名称冲突的可能性,共享程序集都不赋予了一个基于私有密匙加密法加密的名称(私有程序集只需要简单的指定其主文件的名称为其名称就可以了)。这个名称被称作“强名“,保证唯一,并且每个引用共享程序集的应用程序都必须提供此名称。

程序集可能被覆盖的问题通过允许同时安装两个不同版本的相同程序集的时候,在程序集清单中记录特定的版本信息来解决的,

1.4.3 反射

因为程序集中存储了元数据,包括所有的类型和这些类型的成员的所有细节都在程序集定义了,所以就可以用程序访问元数据。这其中的所有细节我们将在第10章讨论。这就是可能令人感兴趣的被称作反射的技术,它说明托管代码可以检测其他的托管代码甚至是检测自身,已确定代码的信息。反射经常被用于获得属性的详细信息,当然,你也可以将反射技术用于其它目的,例如通过以字符串的方式提供类或者方法的名字来间接实现类或者调用方法。通过这种方法你可以在运行时根据用户输入才决定需要实例化的类或者需要调用的方法,而不需在程序编译之前就决定。

1.5 .NET Framework类库

至少从一个开发者的观点来看,编写托管代码的一个最大好处就是你可以使用.NET基础类库(base class library)。

.NET基础类就是一大堆已经编写好了的托管代码类的集合,几乎所有以前可以用Windows API来完成的工作她都可以帮你完成。这些类同样也遵守IL所采用的对象模型,基于单继承的对象模型。这就是说你既可以实例化任何一个需要的.NET基础类,或者你可以从其中派生你自己的类。

.NET基础类库的一个巨大的优点就是使用起来非常方便。例如,如果要启动一个线程,你可以直接调用Thread类的Start()方法。如果要销毁一个TextBox,你需要设计TextBox对象的Enabled属性为false。虽然Visual Basic 和 Java的开发者很熟悉这种使用方便的类库,但是对于C++开发者来说这已经带来了极大的解脱,毕竟多年来他们一直在使用像GetDIBits( ), RegisterWndClassEx( ), 和 IsEqualIID( )这样的API函数,而且在其间需要传递很多的Windows handles。

另一方面,C++开发者总是可以方便的访问整个Windows API,而Visual Basic 6 和 Java开发者通过各自的语言访问操作系统的基础功能就会受到很多限制。.NET基础类库的创新就在于它将类似Visual Basic 和 Java类库使用的便利性和覆盖整个Windows API功能的全面性结合起来。当然,仍然有一些Windows的特性我们无法通过使用基础类库得到,必须使用API功能才能实现,但是,一般来说,那些使用不到的都是些特殊的特性。对于每天的应用而言,基础类库已经是足够的了。如果你确实需要调用API功能的话,不管你是在使用C#, C++, 或者 Visual Basic .NET,.NET也提供了所谓“platform- invoke“,以确保数据类型被正确的转换,这样这种调用的工作也没有在C++中直接调用函数困难。

注意:WinCV,一个基于Windows实用程序,可以用来游览基础类库中的类,结构,接口和枚举类型。我们将要在第12章讨论WinCV。

尽管从题目上看第三章是用来介绍基础类库这个主题的,但是实际上,当我们完成了C#语言的语法学习后,这本书的主要任务就是教给你如何使用.NET基础类库中的各种各样的类了。这是一个非常广泛的基础类库!作为一个大致的介绍,.NET的基础类库包括:

❑ IL提供的核心特性(包括,基本数据类型和CTS,具体见第3章)
❑ Windows GUI的支持和控制(见第19章)
❑ Web Forms (ASP.NET, 在第25 至 27章讨论)
❑ 数据访问(ADO.NET, 见第 21 和 22章)
❑ 目录访问(见第24章)
❑ 文件系统和注册表访问(见第30章)
❑ 网络和Web游览(见第31章)
❑ .NET属性和反射(见第10章)
❑ 访问Windows操作系统(环境变量等,见第14章)
❑ COM互操作性(见第28和29章)

附带说明,根据微软的源文件,.NET基础类库中的大多数代码实际上都是用C#编写的!

1.5.1 命名空间

命名空间是.NET消除类和类之间名称冲突的办法。他是用来避免如下状况的:你定义一个表示消费者的类,并且给她取名叫做Customer,而正好有另一个人也是这样做的,这样就引起了冲突(想象一下,在一个有很多消费者的商业中)。

命名空间只是一组数据类型,但是命名空间里的数据类型会自动在自己的名字前加上命名空间的名字作为前缀。而且命名空间是可以互相嵌套的。比如,大多数用于一般目的的.NET基础类都被放在一个称为System的命名空间里。基础类Array就在这个命名空间里,所以数组的全名是System.Array。

.NET要求所有的数据类型都要定义在命名空间里,例如你可以将你的Customer类放在一个叫YourCompanyName的命名空间里。这样这个类的全名就是“YourCompanyName.Customer“。

注意:如果没有明确地提供命名空间,那么定义的类型就会被添加到一个没有名字的全局命名空间里。

微软建议,对于大多情况,你应该指定一个至少两层嵌套结构的命名空间名称:第一个代指你的公司名称,第二个用来指代类所存在于的技术或者软件包的名称,例如YourCompanyName.SalesServices.Customer。这样就保证了在大多数情况下你的应用程序的类都不会和其他的组织的类发生名称冲突。

我们将要在第2章更加详细地了解命名空间。

1.6 用C#创建.NET应用程序

C#当然可以用来创建控制台应用程序,一种在DOS窗口下运行的仅有文本的应用程序。你可能会在单元测试类库时用到控制台应用程序,或者在创建Unix 或者 Linux daemon进程时用到。但是,在更多的情况下你将会用C#创建一些利用更多的.NET技术的应用程序。在这一节,我们将要大体了解一下C#究竟可以创建哪些不同类型的应用程序。

1.6.1 创建ASP.NET应用程序

Active Server Pages (ASP)是微软用来创建带有动态内容的Web的技术。一个ASP页面基本上就是一个在HTML中加入大块运行在服务器端的VBScript 或者 JavaScript代码的文件。当一个客户端游览器请求一个ASP页面时,Web服务器处理服务器端代码并连同HTML部分一起发送给客户端。通常,这些脚本会从数据库里查询数据并且用HTML将这些数据标记出来。对于客户来说,ASP是一种创建基于游览器的应用程序的简便方法。

但是,ASP也不是没有缺点。首先,ASP页面显示起来比较慢,因为服务器端代码是解释运行的,而不是编译运行。第二,ASP文件很难被维护,因为他们是无结构的,服务器端ASP代码和普通HTML代码完全混合在一起。第三,有时候开发ASP也是困难的,因为ASP对错误处理和类型检查支持很少。

特别的,如果你希望在你的页面中用VBScript实现错误处理,你就必须使用On Error Resume Next声明,并且跟踪检查Err.Number以确定每个组件的调用有运行正常。

ASP.NET是一个全新版本的ASP,并且修正了的许多问题。ASP.NET并不是为了取代ASP,相反,ASP.NET页面可以和原来的ASP应用程序一起存在于同一个服务器上。当然,你可以用C#编写ASP.NET应用程序。

后面的小节我们将会探索一下ASP.NET的关键特性。如果想要获得更多的细节,请参考第25到27章。

第一部分第一章(22~23页)

1.4.1.1 ASP.NET的特性

首先,也是最重要的,ASP.NET页面是结构化的。这就是说,每一个页面都是一个继承自.NET System.Web.UI.Page的有效类,而且你可以重写一些方法,这些方法会在整个页面的生存其中调用(你可以认为这些方法就像原来在ASP下的global.asa里的OnApplication_Start 和 OnSession_Start事件差不多)。因为你可以直接将页面的功能直接放在事件处理程序中实现,ASP.NET页面很容易理解。

ASP.NET页面的另一个优点是你可以用Visual Studio .NET创建它,你也可以用同样的开发环境创建ASP.NET页面所用到的商业逻辑和数据访问组件。一个Visual Studio .NET项目或者解决方案包含一个应用程序相关联的所有文件。而且,你还可以在这个编辑器里调试你的ASP页面,这可比原来的Visual InterDev要好得多。配置InterDev和工程的Web服务器以令他们支持调试是一件令人头痛的事情。

最清楚的是,ASP.NET的后置代码特性可以使你更进一步的结构化你的方法。ASP.NET允许你将服务器端的功能于页面代码隔离开来,单独放到一个类中,这个类会被编译成DLL文件,并且这个DLL会被放置在HTML部分的下级目录中。在页面的顶不会有一个后置代码的指示将文件和它的DLL连接起来。当游览器请求一个页面的时候,Web服务器就会再页面的后置代码所产生的DLL文件中引发类中的相应事件。

最后,但是也很重要,ASP.NET带来的性能增加是显著的。相比较经典的ASP页面都是解释性相应每个请求而言,Web服务器缓存的是经过编译后的ASP.NET页面。这意味着以后对于ASP.NET页面的请求执行起来就会比以前快得多。

ASP.NET同样也使编写基于游览器的窗体变得简单,这些窗体你可以在企业内网中使用。传统的基于窗体的应用程序提供一个功能丰富的用户界面(富客户端),但是因为这些用户界面程序运行在不同的机器上,所以很难去维护它们。正因为这个原因,当人们既需要一个功能强大的用户界面又需要为用户提供广泛的支持的时候,人们就会依靠基于窗体的应用程序。

但是,随着IE5的到来和Navigator 6的没落,基于窗体的应用程序的优点越来越不明显。IE 5对DHTML一致并且全面的健全的支持,允许程序员创建同原来富客户端环境完全相同的基于Web的应用程序。当然,这样的应用程序在IE是必要的并且标准的,但是Navigator却不支持。在许多工业情况中,标准化现在是正常的。

1.4.1.2 Web Forms

为了使建立Web页面更加容易,Visual Studio .NET提供了Web Forms。它们允许你用像创建Visual Basic 6 或者 C++ Builder窗口的方式图形化的建立ASP.NET页面,通过那种拖动一个toolbox空间放到窗体中的方式,相应的代码就会出现在窗体中,最后你只需要为每个空间的事件编写程序就可以了。当你用C#创建Web窗体的时候,你实际上是在创建一个继承自Page基类的C#类,并且某个ASP.NET页面指定此类是其后置代码。当然,你创建Web窗体不必非采用C#,你也可以使用Visual Basic .NET或者其他的.NET语言都无妨。

在过去,开发Web应用程序的困难使得很多团队都不喜欢开发Web应用程序。如果你想要成功开发Web应用程序,你就必须掌握很多不同的技术,比如VBScript, ASP, DHTML, JavaScript等等。通过将窗体的概念应用于Web页面,Web使得Web开发变得相当的简单。

Web 控件

在Web窗体使用的控件与ActiveX控件不同。它们是ASP.NET命名空间中的一些XML标签,在请求一个页面时,Web游览器动态的将其转换为HTML和客户端脚本。令人惊奇的是,Web服务器可以有好几种方法执行服务器端的控件,根据请求着的游览器的不同而做出相应的转化。这就意味着现在为Web页面编写通用的用户界面是非常简单的,而不用担心你的页面将会运行在什么样的游览器上,因为Web替你考虑这一切。

你可以使用C# 或者 Visual Basic .NET来扩充你的Web窗体工具箱。建立一个新的服务器端控件事非常简单的事情,实现.NET的System.Web.UI.WebControls.WebControl类就可以了。

1.4.1.3 XML Web服务

今天,HTML页面占据了World Wide Web的大多数应用。但是,通过XML计算机有了一种独立于设备的在Web上相互通信的方式。将来,计算机可以利用Web 和 XML来交换信息而不是采用专用线路和专有的格式,例如Electronic Data Interchange (EDI)。XML Web服务是为面向服务Web而设计的,在最终显示给用户之前,远程计算机可以互相提供动态的信息并且分析和重新格式化已提供给用户。XML Web服务以XML的形式提供了一种计算机之间互相提供信息的简便方法。

在技术上说,.NET上的XML Web服务就是一种返回XML而不是HTML给客户端的ASP.NET页面。这样的页面饱含一个派生自Web Service类的后置代码的DLL。Visual Studio .NET IDE提供了一个简化Web服务开发的引擎。

一个组织主要有两个理由采用XML Web服务。第一个原因是XML Web服务是依赖于HTTP的,XML Web服务可以利用现有的网络来作为信息传输的媒介。另一个原因是XML Web服务所使用的数据格式XML是自我描述的、非专用的、平台独立性的。

1.4.2 创建Windows Forms

尽管C# 和 .NET非常适合开发Web开发,但是他们仍然为开发所谓的胖客户端或者富客户端应用程序提供了良好支持,这些程序都必须被安装到最终用户的机器上并且在那里执行。这些功能都是由Windows Forms提供的。

版权声明:CSDN是本Blog托管服务提供商。如本文牵涉版权问题,CSDN不承担相关责任,请版权拥有者直接与文章作者联系解决。

posted on 2004年11月02日 12:19 AM



回复:Wrox的C#高级编程第三版第一部分第一章(22~23页) 2004-11-02 2:51 PM tonyduke

译者你不要生气,我只是客观地说一下我看您的译作的感受。如果您看过钱歌川先生的《翻译的技巧》,或者读过任何一本大学英汉翻译教科书,我想您的作品不至于这么晦涩和支离破碎。翻译是一项系统工程,您没有发现您的句子依然是英文的语序结构么?如果让您用中文作一篇文章,您这样的作品,恐怕连及格都不容易。忠言逆耳,良药苦口,我不是专门来给您找碴的。我仅仅给你提一些意见,我想既然你敢把译作放出来,就已经做好了挨骂的思想准备。夸奖一个人很容易,但有什么用呢?我不知道您的知识背景,不过我的建议是,先别急着翻译,买一本大学英汉翻译教程和一本微软英汉计算机词典。看过之后,您再看看您现在的译作。您就会知道,我的评价不过分。

回复:Wrox的C#高级编程第三版第一部分第一章(22~23页) 2004-11-02 9:09 PM 漂流瓶

呵呵,tonyduke不知道是研究什么的,很想切磋一下。不过,我觉得COOFUCOO本意就是想自己学习C#原著的同时提高自己的英语。他翻译整个作品的目的并不是要名扬于世,只是为了锻炼和提高自己的能力。所以在一个人提高和进步的道路上需要他人的扶持,也需要想有经验的人学习取经。您对他的翻译意见,我也有些同意。不过,他翻译的作品也正在不断地完善之中,要是您有什么意见一起提出来,这样也可以教学相长,翻译出来的东西也更会被他人认同。

Contributors: FHL