现在的位置: 首页 > 综合 > 正文

.NET存在哲学

2014年09月05日 ⁄ 综合 ⁄ 共 8294字 ⁄ 字号 评论关闭

1.      写在前面... 1

2.      认清.NET之前的世界... 1

2.1         CWIN32 API的程序人生... 1

2.2         C++/MFC的程序人生... 1

2.3         VB6的程序人生... 2

2.4         Java/J2EE的程序人生... 2

2.5         COM的程序人生... 2

2.6         Windows DNA的程序人生... 3

3.      The .NET Solution. 3

3.1         对已有的代码的完全可操作性... 3

3.2         完全的语言集成... 3

3.3         所有的.NET语言共用同一个通用运行时引擎... 3

3.4         一个大型的基类库... 3

3.5         不再需要COM组装... 3

3.6         一个真正简单的部署模型... 3

4.      .NET的基本构造块... 3

5.      先瞄了一下程序集... 4

6.      单文件和多文件程序集... 4

7.      程序集的首位成员——中间语言... 5

7.1         语言集成... 5

7.2         平台无关性... 5

7.3         一次即时编译JIT.. 5

8.      程序集的第二位成员——元数据metadata. 5

9.      程序集的第三位成员——程序集清单manifest 6

10.              已有代码库的组织——程序集与名字空间到类型的区分... 6

11.              引用外部程序集与GAC.. 7

1.      写在前面

近几年来,当代的程序员为了跟上当今技术的步伐不得不一次又一次的自我知识的移稙和更新。语言(C++, Visual Basic 6.0, Java)、框架(MFC, ATL, STL)、架构(COM, CORBA, EJB)一个又一个为了实现软件开发的“银弹”而粉墨登场。.NET则是最新一道风景了。

 

2.      认清.NET之前的世界

在进入.NET讨论之前,对现存的一些问题进行思考是很有益的。什么问题?就是促使.NET平台产生的原因!为了让思想走上正轨,我们还是先上上历史课,把握自己的根本并且明白过去技术的不足。最后我们也就更容易明白.NET平台是如何填补这些不足。

 

2.1      CWIN32 API的程序人生

一般来说,用C语言在window家庭操作系统上开发软件时,是直接和Window提供的API打交道的。无可否认有无数的程序使用这种经典开发方式被成功开发出来了。当然,很少有人不认为直接使用底层API是一件复杂的活。

C语言的第一个问题是它过于简单。C程序员不得不手动进行内存管理、复杂的指针运算和句法构造。还有,C语言是结构化语言,没OO特征。当你把Win32 API定义的数以千计的全局函数和数据类型加进来后,C语言已经成了一种可怕的语言了,再加上当今还有不少长满虫子的程序浮出水面,可怕吧!

 

2.2      C++/MFC的程序人生

C++对基于C/API的软件开发模式作了一个很大的提升。C++从很多方面看可以想像成在C的层面上再搭建一层面向对象层次。尽管C++提供了OOP的三大主柱,依然保留了C的“痛”——手动进行内存管理、复杂的指针运算和句法构造。

这个复杂性促使不少C++框架出台。如MFCMFC为程序员提供了一组类用以简化Win32程序的开发。MFC的主要角色是把一组合理相关的Win32 API包装成类、宏和很多代码生成工具(叫wizards)。事实已经证明,即使有MFC和一些辅助的工具,C++C的复杂性仍然未成降低多少。

 

2.3      VB6的程序人生

简单在一定场合还是很值得推崇的。不少程序员(尤其是初学者)从VB世界获得了这种简单性。VB6很流行,原因在于它用最少的功夫实现了,一创建复杂的用户界面,二包装代码库(如COM组件),三是写数据访问逻辑。VB是如何做到把Win32 API进行隐藏的呢?答案是它提供了大量的集成代码向导、内置数据类型、类和函数。

VB的最大的不足在于,它不是完全的OO语言,它是基于对象的语言。例如,VB不允许程序员在类型之间建立“是一个”的关系,也就是继承!也不内置支持类的参数化构造(不支持吗?)。还有,VB不能开发多进程应用,除非你放弃VB的简单性,回到Win32API

 

2.4      Java/J2EE的程序人生

Java优点有:它是完全的OOPL,沿袭了C语法的精简性。Java最大卖点就是它的平台的独立性。Java作为一种语言,去掉C++一部分烦复的语法,作为一个平台引进“包”的概念降低开发的复杂性。包packages是一组不同的预定义的类型的合体。使用这些“包”,Java程序员可以使用数据库接接、消息支持、Web用可前端和一个丰富的用户界面创建100%的纯Java应用,

Java是一种优雅的语言,唯一不足在于,使用Java意思着在开发的周期内从前台到后台你都要使用Java。(这就是纯?)结果是,Java不提供与其它语言进行集成,因为有违了Java初始目标——单一语言满足所有需要。但实现上,已经有了数以百万的代码放在哪儿,而不必Java自己重写了。可惜Java把不是问题变成问题。

Java简单地说,是不合适用于开发重在图型上和数字上的应用。唯一的选择是底层的语言,如C++。一个可以确实的事实是,Java不善于访问非JavaAPI,也就是说它对跨语言集成支持度有限。

 

2.5      COM的程序人生

COM组件对象模型是Microsoft上一个的应用开发框架。从效果上看,COM是一种架构:如果你使用COM的一致的规则创建你的类型,那么你也在二进制层次创建了一个可复用的模块。

COM二制进组件的美丽之处在于它的语言独立性,也就说,C++程序员创建的COM类可以被VB6调用。Delphi程序员调用由C创建的COM类等。不过你可能已经注意到了,COM的语言独立性有一些限制。例如:没有办法对已有的COM类进行继承(因为COM本身是不支持典型的继承机制的)。因此你不得不用更为烦锁的“有一个”的方法重用COM类型。

COM二制进组件的第二个美丽之处在于位置的透明性。使用诸如程序标识AppIDs、存根stubs、代理proxiesCOM的运行时环境(啊!?),程序员可以省掉与底层的socketsRPC调用和其它的底层的细节。看看下面的VB COM客户代码:

' This block of VB6 code can activate a COM class written in

' any COM-aware language, which may be located anywhere

' on the network (including your local machine).

Dim c as MyCOMClass

Set c = New MyCOMClass ' Location resolved using AppID.

c.DoSomeWork

尽管COM被认为是一个非常成功的对象模型,但是头巾的背后过于复杂了(当你是一位C++程序员,在数月的COM的研究后你就会得到这样的结果)!像降低C++的复杂性类似,为了降低COM的复杂性,相关的框架又出来了。如活动模板库ATLATL提供了一组简化创建COM类型的类、模板和宏。很多其它语言也作出了努力,把大部份COM的基础架构隐藏起来(如VB的很多相关技术都是基于COM的)。不过,单单是语言是足以隐藏COM的所有复杂性的,比如你选择相对较简单的COM语言VB6,你仍然不得不面对脆弱的组件注册问题和很多与部署有关的问题,比如有名的DLL地狱。

 

2.6      Windows DNA的程序人生

互联网的出现为程序开引入更多的复杂性。近几年,Microsoft已经为它的操作系统家族和产品添加面向Internet的功能。很可惜,使用基于COMWindows DNA开发Web应用仍然比较复杂!

复杂性源自一个很简单的事实,Windows DNA需要很多相关技术和语言:ASP, HTML, XML, JavaScript, VBScript, COM(+),还有数据访问API,如:ADO。问题出在,这些技术在语法层面上看是完全不相关的。例如,Javascipt的语法像CVBscript则像VB6COM服务器运行在COM+运行时环境时与在被ASP页调用时完全两个样儿!结果是一个让人迷惑的技术的大杂烩。

一个更重的东西,就是每一种语言或每一种技术都使用自己的类型系统。Javascript整型数据与VB6是不完全一致。

 

3.      The .NET Solution

上完历史课后,我们来看看.NET Framework是怎样的一个完全新的模型来改善我们的生活。下面的.NET Framework的精简后的核心功能特色:

3.1      对已有的代码的完全可操作性

这是一个很好东西。已经的COM组件可以和新的.NET二进制组件共存。而且平台激活服务PInvoke可以让你在.NET的代码里调用基于C的库(包括操作系统的API)。

3.2      完全的语言集成

COM不同,.NET支持跨语言继承、跨语言异常处理和跨语言的调错。

3.3      所有的.NET语言共用同一个通用运行时引擎

这个引擎是一组“定义良好”的类型,而每一种.NET语言都能“明白”这些类型

3.4      一个大型的基类库

这个库除了像历来的库一样隐藏了底层的复杂性处,更重要的是这继所有的.NET语言提供了一致的对象模型。

3.5      不再需要COM组装

IClassFactory, IUnknown, IDispatch, IDL代码,和万恶之源VARIANT数据类型不会再在.NET组件中使用了。

3.6      一个真正简单的部署模型

有了.NET,二进制组件再也不用注册到系统注册表了。还有,现在.NET允许同一个.dll的不同版本存活在同一台机器上了。

4.      .NET的基本构造块

前的“理想”都都有赖于三C.NET的基本构造块CLRCTSCLS!

.NET可以简单地被理解一个新的运行时环境加一个新类库!

u        CLR语言运行时就是一个环境,它负责定位、加载和管理.NET类型,管理底层工作如内存管理、安全检查等。

u        CTS类型系统则是一项标准,它描述了运行时所支持的类型和编程结构,指定类型间如何交互,也规定metadata的格式。

u        CLS语言标准也是一项标准,它定义一个通用类型和编程结构的子集,让所有的.NET语言都要明白,从而可相互交互。

与以前一般类库,.NET基类库异常巨大,因为包含开发不同应用的方面的类,比如数据访问、安全控制、XML操作和Web前端控件等。


5.      先瞄了一下程序集

.NET binaries COM server和非托管Win32 binaries相比虽然都是dll,内部其实完全不同的。

最大的不同在于.NET binaries包含是中间语言代码和自描述的约定格式metadata

当你用.NET编译器创建一个.dll.exe后,该模块将被划入这个程序集。这什么意思呢?

IDL对干metadata

The problems with COM type information are that it is not guaranteed to be present and the fact that IDL code has no way to document the externally referenced servers that are required for the correct operation of the current COM server. In contrast, .NET metadata is always present and is automatically generated by a given .NET-aware compiler.

COM类型信息不能被保证可用和IDL没有办法描述目前的COM server所依赖的外部引用。是这样的吗?

assembly manifest程序集清单是描述程序集本身的信息,比如,程序集版本信息、区域信息和引用外部程序集列表等。

 

6.      单文件和多文件程序集

很多时候一个程序集是对应于一个dll文件的,在这个时候可以说,程序集是那个dll二进制文件。不过这不完全确切的。从技术角度讲,如果一个程序集是只由单一的.dll.exe模块组成,那么我们说这是一个“单文件程序集”。

单文件程序集是一个自治单一包,这个包包包含所有必须的CILmetadata和相关的程序集清单manifest

Multifile assemblies, on the other hand, are composed of numerous .NET binaries, each of which is termed a module. When building a multifile assembly, one of these modules (termed the primary module) must contain the assembly manifest (and possibly CIL instructions and metadata for various types). The other related modules contain a module level manifest, CIL, and type metadata. As you might suspect, the primary module documents the set of required secondary modules within the

assembly manifest.

多文件程序集则有点不同,它由很多个被名之模块的.NET二进制文件(一般是dll)组成。当为程序集创建程序集清单manifest时,其中一个被定为主模块的模块包含了程序集清单(有CIL代码和各不同类型的metadata),其它的模块成员则包含自己模块级的程序集清单。

把程序集按模块分为不同文件是为了灵活部署,提高性能。当一个用户引用一个远程程序集时时,他只需要下载需的模块就可以了,不必下载整个程序集。

注意:导入名字空间与导入装配件的区别

导入名字空间只是为让编译器能够找到相应的类型,并不会自动链接相关的装配件(当然要记得系统要自动链接一部分默认的装配件),使用@Import指只是为了使用短名机制,如果类型所属的装配件没被加载,@Import于事无补!编译器不能找到类型信息。

 你或许已经注意到,装配件和名字空间似乎一样的东西,但事实上这只是偶然发生的,记住两者实际上是完全不同的东西,从它们使用不同的指示指令可以看到这一点

由此可见,程序集实际上是一个或多个模块的逻辑集合,这些模块可以单一地部署和版本控制。

 

7.      程序集的首位成员——中间语言

7.1      语言集成

每一种不同.NET语言的编译器都生成一样的中间代码,所以各种.NET语言可以很好的交互。

7.2      平台无关性

CIL中间语言是平台无关的,所以.NET框架也是平台无关的。这一点就像Java一样。不过,有一点不同的是,.NET还是语言无关的,跨语言的,程序员可以选择自己喜欢的.NET语言。

7.3      一次即时编译JIT

.NET运行时环境会为不同的CPU和不同的平台准备相应的Jitter编译器。例如,如果你为手持设备开发一个应用,那么相应的Jitter则运行在低内存环境。相反,如果你在一个大型的服务器上部署你的程序集,则Jitter会优化使用在大内存的功能。

 

8.      程序集的第二位成员——元数据metadata

元数据精确地描述了定义在程序集内的每一种类型(类、结构、枚举等),和类型的每一位成员(属性、方法、事件等)。这些有关类型的元数据信息是由编译自动为我们生成的,不必自己动手。元数的完备性至使程序集是一个完全自我描述的,所以不用为程序集进行注册操作。

格式如下:

TypeDef #2 (02000003)

-------------------------------------------------------

TypDefName: CalculatorExample.Calc (02000003)

Flags : [Public] [AutoLayout] [Class]

[AnsiClass] [BeforeFieldInit] (00100001)

Extends : 01000001 [TypeRef] System.Object

Method #1 (06000003)

-------------------------------------------------------

MethodName: Add (06000003)

Flags : [Public] [HideBySig] [ReuseSlot] (00000086)

RVA : 0x00002090

ImplFlags : [IL] [Managed] (00000000)

CallCnvntn: [DEFAULT]

hasThis

ReturnType: I4

2 Arguments

Argument #1: I4

Argument #2: I4

2 Parameters

(1) ParamToken : (08000001) Name : x flags: [none] (00000000)

(2) ParamToken : (08000002) Name : y flags: [none] (00000000)

元数据被用于.NET运行时的方方面面,包括在不同的开发工具里也使用它。例如VS.NET的智能感应IntelliSense就是在设计时读取元数据实现智能感应的。可以很确定的是,元数据metadataremoting、反射reflection、晚挷定、XML Web service和对象序列化等.NET技术的主骨干。

 

9.      程序集的第三位成员——程序集清单manifest

前面已经说了,程序集清单manifest包括描述程序集本身的元数据。这些有,当前程序集所依赖的外部程序集列表、版本号和版权信息等。程序集清单也是由编译器自动生成的。看下面:

.assembly extern mscorlib

{

.publickeytoken = (B7 7A 5C 56 19 34 E0 89 )

.ver 2:0:0:0

}

.assembly CSharpCalculator

{

.hash algorithm 0x00008004

.ver 0:0:0:0

}

.module CSharpCalculator.exe

.imagebase 0x00400000

.subsystem 0x00000003

.file alignment 512

.corflags 0x00000001

 

10.已有代码库的组织——程序集与名字空间到类型的区分

我们都知道代码库对程序员是很重要的。我们MFCJ2EEATL等都类库,它们是怎么组织呢?他说MFC等是对现有的代码的一组定义良好的集合。(回来再看了,没有用过).NET基类库使用名字空间进行组织。

.NET里的名字空间的定义:名字空间是一组在程序集里相关类型。这里有三个对象,名字空间、程序集和类型。要注意一点,单一的程序集可以包含有任意多个名字空间,而一个名字空间也可以包含有任意多个类型。一个很好的例子就是.NET基类库。.NET基类库被划份为多个离散的程序集。最主要的程序集是mscorlib.dllmscorlib.dll又按不的名字空间划分很多核心的类型(之所说它是核心是因为这些类型包括了封装各种不同的常见编程工作的类型和.NET语言内建数据类型)

.NET代码库与MFC等代码库的一最关键的不同的是,MFC代码库是语言相关的,而所有的.NET都使用一样的名字空间,一样的类型。

作为一个.NET程序员,他的主要目标是从这些空间里掏宝,掌握空间里的类型的价值。不过首要的是System名字空间。

 

11.引用外部程序集与GAC

我需要一个类型,引用一个名字空间,名字空间在哪?在程序集。程序集在哪?在……皮之不存,毛将焉附!我们需要的是类型的中间代码定义(也就是代码所在的程序集),而不是一个逻辑的名字。当你使用一个非默认的程序集的时候,你还是告诉编译器你的程序集在哪的。大部分主要的.NET框架的程序集都放在一个称为全局程序集缓存global assembly cache (GAC)的目录里。

 

 

 

抱歉!评论已关闭.