1.?????
寫在前面
近幾年來,當代的程序員為了跟上當今技術的步伐不得不一次又一次的自我知識的移稙和更新。語言
(C++, Visual Basic 6.0, Java)
、框架
(MFC, ATL, STL)
、架構
(COM, CORBA, EJB)
一個又一個為了實現軟件開發的“銀彈”而粉墨登場。
.NET
則是最新一道風景了。
?
2.?????
認清
.NET
之前的世界
在進入
.NET
討論之前,對現存的一些問題進行思考是很有益的。什么問題?就是促使
.NET
平臺產生的原因!為了讓思想走上正軌,我們還是先上上歷史課,把握自己的根本并且明白過去技術的不足。最后我們也就更容易明白
.NET
平臺是如何填補這些不足。
?
2.1?????
C
和
WIN32 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++
框架出臺。如
MFC
;
MFC
為程序員提供了一組類用以簡化
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
不善于訪問非
Java
的
API
,也就是說它對跨語言集成支持度有限。
?
2.5?????
COM
的程序人生
COM
組件對象模型是
Microsoft
上一個的應用開發框架。從效果上看,
COM
是一種架構:如果你使用
COM
的一致的規則創建你的類型,那么你也在二進制層次創建了一個可復用的模塊。
COM
二制進組件的美麗之處在于它的語言獨立性,也就說,
C++
程序員創建的
COM
類可以被
VB6
調用。
Delphi
程序員調用由
C
創建的
COM
類等。不過你可能已經注意到了,
COM
的語言獨立性有一些限制。例如:沒有辦法對已有的
COM
類進行繼承(因為
COM
本身是不支持典型的繼承機制的)。因此你不得不用更為煩鎖的“有一個”的方法重用
COM
類型。
COM
二制進組件的第二個美麗之處在于位置的透明性。使用諸如程序標識
AppIDs
、存根
stubs
、代理
proxies
和
COM
的運行時環境(啊!?),程序員可以省掉與底層的
sockets
,
RPC
調用和其它的底層的細節。看看下面的
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
的復雜性,相關的框架又出來了。如活動模板庫
ATL
。
ATL
提供了一組簡化創建
COM
類型的類、模板和宏。很多其它語言也作出了努力,把大部份
COM
的基礎架構隱藏起來(如
VB
的很多相關技術都是基于
COM
的)。不過,單單是語言是足以隱藏
COM
的所有復雜性的,比如你選擇相對較簡單的
COM
語言
VB6
,你仍然不得不面對脆弱的組件注冊問題和很多與部署有關的問題,比如有名的
DLL
地獄。
?
2.6?????
Windows DNA
的程序人生
互聯網的出現為程序開引入更多的復雜性。近幾年,
Microsoft
已經為它的操作系統家族和產品添加面向
Internet
的功能。很可惜,使用基于
COM
的
Windows DNA
開發
Web
應用仍然比較復雜!
復雜性源自一個很簡單的事實,
Windows DNA
需要很多相關技術和語言:
ASP, HTML, XML, JavaScript, VBScript,
和
COM(+)
,還有數據訪問
API
,如:
ADO
。問題出在,這些技術在語法層面上看是完全不相關的。例如,
Javascipt
的語法像
C
,
VBscript
則像
VB6
。
COM
服務器運行在
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
的基本構造塊
CLR
、
CTS
、
CLS!
.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
模塊組成,那么我們說這是一個“單文件程序集”。
單文件程序集是一個自治單一包,這個包包包含所有必須的
CIL
、
metadata
和相關的程序集清單
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
就是在設計時讀取元數據實現智能感應的。可以很確定的是,元數據
metadata
是
remoting
、反射
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.
已有代碼庫的組織——程序集與名字空間到類型的區分
我們都知道代碼庫對程序員是很重要的。我們
MFC
、
J2EE
、
ATL
等都類庫,它們是怎么組織呢?他說
MFC
等是對現有的代碼的一組定義良好的集合。(回來再看了,沒有用過)
.NET
基類庫使用名字空間進行組織。
.NET
里的名字空間的定義:名字空間是一組在程序集里相關類型。這里有三個對象,名字空間、程序集和類型。要注意一點,單一的程序集可以包含有任意多個名字空間,而一個名字空間也可以包含有任意多個類型。一個很好的例子就是
.NET
基類庫。
.NET
基類庫被劃份為多個離散的程序集。最主要的程序集是
mscorlib.dll
。
mscorlib.dll
又按不的名字空間劃分很多核心的類型(之所說它是核心是因為這些類型包括了封裝各種不同的常見編程工作的類型和
.NET
語言內建數據類型)
.NET
代碼庫與
MFC
等代碼庫的一最關鍵的不同的是,
MFC
代碼庫是語言相關的,而所有的
.NET
都使用一樣的名字空間,一樣的類型。
作為一個
.NET
程序員,他的主要目標是從這些空間里掏寶,掌握空間里的類型的價值。不過首要的是
System
名字空間。
?
11.
引用外部程序集與
GAC
我需要一個類型,引用一個名字空間,名字空間在哪?在程序集。程序集在哪?在……皮之不存,毛將焉附
!
我們需要的是類型的中間代碼定義(也就是代碼所在的程序集),而不是一個邏輯的名字。當你使用一個非默認的程序集的時候,你還是告訴編譯器你的程序集在哪的。大部分主要的
.NET
框架的程序集都放在一個稱為全局程序集緩存
global assembly cache (GAC)
的目錄里。