




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
1、C#高級編程(權威版)第1章.NET體系結構我們不能孤立地使用C#語言,而必須和.NET Framework一起考慮。C#編譯器專門用于.NET,這表示用C#編寫的所有代碼總是在.NET Framework中運行。對于C#語言來說,可以得出兩個重要的結論:(1) C#的結構和方法論反映了.NET基礎方法論。(2) 在許多情況下,C#的特定語言功能取決于.NET的功能,或依賴于.NET基類。由于這種依賴性,在開始使用C#編程前,了解.NET的結構和方法論就非常重要了,這就是本章的目的。下面是本章的容: 本章首先了解在.NET編譯和運行所有的代碼(包括C#)時通常會出現什么情況。
2、0;對這些容進行概述之后,就要詳細闡述Microsoft中間語言(Microsoft Intermediate Language,MSIL或簡稱為IL),.NET上所有編譯好的代碼都要使用這種語言。本章特別要介紹IL、通用類型系統(tǒng)(Common Type System,CTS)與公共語言規(guī)(Common Language Specification,CLS)如何提供.NET語言之間的互操作性。最后解釋各種語言如何使用.NET,包括Visual Basic和C+。 之后,我們將介紹.NET的其他特性,包括程序集、命名空間和.NET基類。 最后本章簡要探討一下C#開發(fā)人員可以創(chuàng)
3、建的應用程序類型。1.1 C#與.NET的關系C#是一種相當新的編程語言,C#的重要性體現在以下兩個方面: 它是專門為與Microsoft的.NET Framework一起使用而設計的。(.NET Framework是一個功能非常豐富的平臺,可開發(fā)、部署和執(zhí)行分布式應用程序)。 它是一種基于現代面向對象設計方法的語言,在設計它時,Microsoft還吸取了其他類似語言的經驗,這些語言是近20年來面向對象規(guī)則得到廣泛應用后才開發(fā)出來的。有一個很重要的問題要弄明白:C#就其本身而言只是一種語言,盡管它是用于生成面向.NET環(huán)境的代碼,但它本身不是.NET的一部分
4、。.NET支持的一些特性,C#并不支持。而C#語言支持的另一些特性,.NET卻不支持(例如運算符重載)!但是,因為C#語言是和.NET一起使用的,所以如果要使用C#高效地開發(fā)應用程序,理解Framework就非常重要,所以本章將介紹.NET的涵。1.2 公共語言運行庫.NET Framework的核心是其運行庫的執(zhí)行環(huán)境,稱為公共語言運行庫(CLR)或.NET運行庫。通常將在CLR的控制下運行的代碼稱為托管代碼(managed code)。但是,在CLR執(zhí)行編寫好的源代碼之前,需要編譯它們(在C#中或其他語言中)。在.NET中,編譯分為兩個階段:(1) 把源代碼編譯為Microso
5、ft中間語言(IL)。(2) CLR把IL編譯為平臺專用的代碼。這個兩階段的編譯過程非常重要,因為Microsoft中間語言(托管代碼)是提供.NET的許多優(yōu)點的關鍵。Microsoft中間語言與Java字節(jié)代碼共享一種理念:它們都是低級語言,語法很簡單(使用數字代碼,而不是文本代碼),可以非??焖俚剞D換為部機器碼。對于代碼來說,這種精心設計的通用語法有很重要的優(yōu)點:平臺無關性、提高性能和語言的互操作性。1.2.1 平臺無關性首先,這意味著包含字節(jié)代碼指令的同一文件可以放在任一平臺中,運行時編譯過程的最后階段可以很容易完成,這樣代碼就可以運行在特定的平臺上。換言之,編譯為中間語言就
6、可以獲得.NET平臺無關性,這與編譯為Java字節(jié)代碼就會得到Java平臺無關性是一樣的。1.2.2 提高性能前面把IL和Java做了比較,實際上,IL比Java字節(jié)代碼的作用還要大。IL總是即時編譯的(稱為JIT編譯),而Java字節(jié)代碼常常是解釋性的,Java的一個缺點是,在運行應用程序時,把Java字節(jié)代碼轉換為部可執(zhí)行代碼的過程會導致性能的損失(但在最近,Java在某些平臺上能進行JIT編譯)。JIT編譯器并不是把整個應用程序一次編譯完(這樣會有很長的啟動時間),而是只編譯它調用的那部分代碼(這是其名稱由來)。代碼編譯過一次后,得到的部可執(zhí)行代碼就存儲起來,直到退出該應用程
7、序為止,這樣在下次運行這部分代碼時,就不需要重新編譯了。Microsoft認為這個過程要比一開始就編譯整個應用程序代碼的效率高得多,因為任何應用程序的大部分代碼實際上并不是在每次運行過程中都執(zhí)行。使用JIT編譯器,從來都不會編譯這種代碼。這解釋了為什么托管IL代碼的執(zhí)行幾乎和部機器代碼的執(zhí)行速度一樣快,但是并沒有說明為什么Microsoft認為這會提高性能。其原因是編譯過程的最后一部分是在運行時進行的,JIT編譯器確切地知道程序運行在什么類型的處理器上,可以利用該處理器提供的任何特性或特定的機器代碼指令來優(yōu)化最后的可執(zhí)行代碼。傳統(tǒng)的編譯器會優(yōu)化代碼,但它們的優(yōu)化過程是獨立于代碼所運行的特定處理
8、器的。這是因為傳統(tǒng)的編譯器是在發(fā)布軟件之前編譯為部機器可執(zhí)行的代碼。即編譯器不知道代碼所運行的處理器類型,例如該處理器是x86兼容處理器還是Alpha處理器,這超出了基本操作的圍。例如Visual Studio 6為一般的奔騰機器進行了優(yōu)化,所以它生成的代碼就不能利用奔騰 III處理器的硬件特性。相反,JIT編譯器不僅可以進行Visual Studio 6所能完成的優(yōu)化工作,還可以優(yōu)化代碼所運行的特定處理器。1.2.3 語言的互操作性使用IL不僅支持平臺無關性,還支持語言的互操作性。簡而言之,就是能將任何一種語言編譯為中間代碼,編譯好的代碼可以與從其他語言編譯過來的代碼進行交互操作
9、。那么除了C#之外,還有什么語言可以通過.NET進行交互操作呢?下面就簡要討論其他常見語言如何與.NET交互操作。1. Visual Basic 2008Visual Basic 6在升級到Visual Basic .NET 2002時,經歷了一番脫胎換骨的變化,才集成到.NET Framework的第一版中。Visual Basic 語言對Visual Basic 6進行了很大的演化,也就是說,Visual Basic 6并不適合運行.NET程序。例如,它與COM的高度集成,且只把事件處理程序作為源代碼顯示給開發(fā)人員,大多數后臺代碼不能用作源代碼。另外,它不支持繼承,Visual Basic
10、使用的標準數據類型也與.NET不兼容。Visual Basic 6在2002年升級為Visual Basic .NET,對Visual Basic進行的改變非常大,完全可以把Visual Basic當作是一種新語言。現有的Visual Basic 6代碼不能編譯為Visual Basic 2008代碼(或Visual Basic .NET 2002、2003和2005代碼),把Visual Basic 6程序轉換為Visual Basic 2008時,需要對代碼進行大量的改動,但大多數修改工作都可以由Visual Studio 2008(Visual Studio的升級版本,用于與.NET一起
11、使用)自動完成。如果把Visual Basic 6項目讀到Visual Studio 2008中,Visual Studio 2008就會升級該項目,也就是說把Visual Basic 6源代碼重寫為Visual Basic 2008源代碼。雖然這意味著其中的工作已大大減輕,但用戶仍需要檢查新的Visual Basic 2008代碼,以確保項目仍可正確工作,因為這種轉換并不十分完美。這種語言升級的一個副作用是不能再把Visual Basic 2008編譯為部可執(zhí)行代碼了。Visual Basic 2008只編譯為中間語言,就像C#一樣。如果需要繼續(xù)使用Visual Basic 6編寫程序,就可
12、以這么做,但生成的可執(zhí)行代碼會完全忽略.NET Framework,如果繼續(xù)把Visual Studio作為開發(fā)環(huán)境,就需要安裝Visual Studio 6。2. Visual C+ 2008Visual C+ 6有許多Microsoft對Windows的特定擴展。通過Visual C+ .NET,又加入了更多的擴展容,來支持.NET Framework。現有的C+源代碼會繼續(xù)編譯為部可執(zhí)行代碼,不會有修改,但它會獨立于.NET運行庫運行。如果讓C+代碼在.NET Framework中運行,就可以在代碼的開頭添加下述命令:#using <mscorlib.dll>還可以把標記/c
13、lr傳遞給編譯器,這樣編譯器假定要編譯托管代碼,因此會生成中間語言,而不是部機器碼。C+的一個有趣的問題是在編譯托管代碼時,編譯器可以生成包含嵌本機可執(zhí)行代碼的IL。這表示在C+代碼中可以把托管類型和非托管類型合并起來,因此托管C+代碼:class MyClass定義了一個普通的C+類,而代碼:_ref class MyClass生成了一個托管類,就好像使用C#或Visual Basic 2008編寫類一樣。實際上,托管C+比C#更優(yōu)越的一點是可以在托管C+代碼中調用非托管C+類,而不必采用COM交互功能。如果在托管類型上試圖使用.NET不支持的特性(例如,模板或類的多繼承),編譯器就會出現一
14、個錯誤。另外,在使用托管類時,還需要使用非標準的C+特性。因為C+允許低級指針操作,C+編譯器不能生成可以通過CLR存類型安全測試的代碼。如果CLR把代碼標識為存類型安全是非常重要的,就需要用其他一些語言編寫源代碼,例如C# 或Visual Basic 2008。3. COM和COM+從技術上講,COM 和 COM+并不是面向.NET的技術,因為基于它們的組件不能編譯為IL(但如果原來的COM組件是用C+編寫的,使用托管C+,在某種程度上可以這么做)。但是,COM+仍然是一個重要的工具,因為其特性沒有在.NET中完全實現。另外,COM組件仍可以使用-.NET集成了COM的互操作性,從而使托管代
15、碼可以調用COM組件,COM組件也可以調用托管代碼(見第24章)。在一般情況下,把新組件編寫為.NET組件,大多是為了方便,因為這樣可以利用.NET基類和托管代碼的其他優(yōu)點。1.3 中間語言如前所述,Microsoft中間語言顯然在.NET Framework中有非常重要的作用。C#開發(fā)人員應明白,C#代碼在執(zhí)行前要編譯為中間語言(實際上,C#編譯器僅編譯為托管代碼),這是有意義的,現在應詳細討論一下IL的主要特征,因為面向.NET的所有語言在邏輯上都需要支持IL的主要特征。下面就是中間語言的主要特征: 面向對象和使用接口 值類型和引用類型之間的巨大差別
16、160; 強數據類型 使用異常來處理錯誤 使用特性(attribute)下面詳細討論這些特征。1.3.1 面向對象和接口的支持.NET的語言無關性還有一些實際的限制。中間語言在設計時就打算實現某些特殊的編程方法,這表示面向它的語言必須與編程方法兼容,Microsoft為IL選擇的特定道路是傳統(tǒng)的面向對象的編程,帶有類的單一繼承性。注意:不熟悉面向對象概念的讀者應參考附錄B,獲得更多的信息。除了傳統(tǒng)的面向對象編程外,中間語言還引入了接口的概念,它們顯示了在帶有COM的Windows下的第一個實現方式。.NET接口與COM接口不同,它們不需要支持任何COM基礎結
17、構,例如,它們不是派生自IUnknown,也沒有對應的GUID。但它們與COM接口共享下述理念:提供一個契約,實現給定接口的類必須提供該接口指定的方法和屬性的實現方式。前面介紹了使用.NET意味著要編譯為中間語言,即需要使用傳統(tǒng)的面向對象的方法來編程。但這并不能提供語言的互操作性。畢竟,C+和Java都使用一樣的面向對象的型,但它們仍不是可交互操作的語言。下面需要詳細探討一下語言互操作性的概念。首先,需要確定一下語言互操作性的含義。畢竟,COM允許以不同語言編寫的組件一起工作,即可以調用彼此的方法。這就足夠了嗎?COM是一個二進制標準,允許組件實例化其他組件,調用它們的方法或屬性,而無須考慮編
18、寫相關組件的語言。但為了實現這個功能,每個對象都必須通過COM運行庫來實例化,通過接口來訪問。根據相關組件的線程模型,不同線程上存空間和運行組件之間要編組數據,這還可能造成很大的性能損失。在極端情況下,組件保存為可執(zhí)行文件,而不是DLL文件,還必須創(chuàng)建單獨的進程來運行它們。重要的是組件要能與其他組件通信,但僅通過COM運行庫進行通信。無論COM是用于允許使用不同語言的組件直接彼此通信,或者創(chuàng)建彼此的實例,系統(tǒng)都把COM作為中間件來處理。不僅如此,COM結構還不允許利用繼承實現,即它喪失了面向對象編程的許多優(yōu)勢。一個相關的問題是,在調試時,仍必須單獨調試用不同語言編寫的組件。這樣就不可能在調試器
19、上調試不同語言的代碼了。語言互操作性的真正含義是用一種語言編寫的類應能直接與用另一種語言編寫的類通信。特別是: 用一種語言編寫的類應能繼承用另一種語言編寫的類。 一個類應能包含另一個類的實例,而不管它們是使用什么語言編寫的。 一個對象應能直接調用用其他語言編寫的另一個對象的方法。 對象(或對象的引用)應能在方法之間傳遞。 在不同的語言之間調用方法時,應能在調試器中調試這些方法調用,即調試不同語言編寫的源代碼。這是一個雄心勃勃的目標,但令人驚訝的是,.NET和中間語言已經實現了這個目標。在調試器上調試方法時,Visual Studio
20、IDE提供了這樣的工具(不是CLR提供的)。1.3.2 相異值類型和引用類型與其他編程語言一樣,中間語言提供了許多預定義的基本數據類型。它的一個特性是值類型和引用類型有明顯的區(qū)別。對于值類型,變量直接保存其數據,而對于引用類型,變量僅保存地址,對應的數據可以在該地址中找到。在C+中,引用類型類似于通過指針來訪問變量,而在Visual Basic中,與引用類型最相似的是對象,Visual Basic 6總是通過引用來訪問對象。中間語言也有數據存儲的規(guī):引用類型的實例總是存儲在一個名為"托管堆"的存區(qū)域中,值類型一般存儲在堆棧中(但如果值類型在引用類型中聲明為字段,
21、它們就聯存儲在堆中)。第2章"C#基礎"討論堆棧和堆,與其工作原理。1.3.3 強數據類型中間語言的一個重要方面是它基于強數據類型。所有的變量都清晰地標記為屬于某個特定數據類型(在中間語言中沒有Visual Basic和腳本語言中的Variant數據類型)。特別是中間語言一般不允許對模糊的數據類型執(zhí)行任何操作。例如,Visual Basic 6開發(fā)人員習慣于傳遞變量,而無需考慮它們的類型,因為Visual Basic 6會自動進行所需的類型轉換。C+開發(fā)人員習慣于在不同類型之間轉換指針類型。執(zhí)行這類操作將大大提高性能,但破壞了類型的安全性。因此,這類操作只能在某
22、些編譯為托管代碼的語言中的特殊情況下進行。確實,指針(相對于引用)只能在標記了的C#代碼塊中使用,但在Visual Basic中不能使用(但一般在托管C+中允許使用)。在代碼中使用指針會立即導致CLR提供的存類型安全性檢查失敗。注意,一些與.NET兼容的語言,例如Visual Basic 2008,在類型化方面的要求仍比較松,但這是可以的,因為編譯器在后臺確保在生成的IL上強制類型安全。盡管強迫實現類型的安全性最初會降低性能,但在許多情況下,我們從.NET提供的、依賴于類型安全的服務中獲得的好處更多。這些服務包括: 語言的互操作性 垃圾收集 安全性
23、; 應用程序域下面討論強數據類型化對這些.NET特性非常重要的原因。1. 語言互操作性中強數據類型的重要性如果類派生自其他類,或包含其他類的實例,它就需要知道其他類使用的所有數據類型,這就是強數據類型非常重要的原因。實際上,過去沒有任何系統(tǒng)指定這些信息,從而成為語言繼承和交互操作的真正障礙。這類信息不只是在一個標準的可執(zhí)行文件或DLL中出現。假定將Visual Basic 2008類中的一個方法定義為返回一個Integer-Visual Basic 2008可以使用的標準數據類型之一。但C#沒有該名稱的數據類型。顯然,我們只能從該類中派生,再使用這個方法,如果編譯器知道如何把Visual Ba
24、sic 2008的Integer類型映射為C#定義的某種已知類型,就可以在C#代碼中使用返回的類型。這個問題在.NET中是如何解決的?(1) 通用類型系統(tǒng)(CTS)這個數據類型問題在.NET中使用通用類型系統(tǒng)(CTS)得到了解決。CTS定義了可以在中間語言中使用的預定義數據類型,所有面向.NET Framework的語言都可以生成最終基于這些類型的編譯代碼。例如,Visual Basic 2008的Integer實際上是一個32位有符號的整數,它實際映射為中間語言類型Int32。因此在中間語言代碼中就指定這種數據類型。C#編譯器可以使用這種類型,所以就不會有問題了。在源代碼中,C#用關鍵字in
25、t來表示Int32,所以編譯器就認為Visual Basic 2008方法返回一個int類型的值。通用類型系統(tǒng)不僅指定了基本數據類型,還定義了一個容豐富的類型層次結構,其中包含設計合理的位置,在這些位置上,代碼允許定義它自己的類型。通用類型系統(tǒng)的層次結構反映了中間語言的單一繼承的面向對象方法,如圖1-1所示。 圖 1-1這個樹形結構中的類型說明如表1-1所示。表 1-1類型含義Type代表任何類型的基類Value Type代表任何值類型的基類Reference Types通過引用來訪問,且存儲在堆中的任何數據類型Built-in Value Types包含大多
26、數標準基本類型,可以表示數字、Boolean值或字符Enumerations枚舉值的集合User-defined Value Types在源代碼中定義,且保存為值類型的數據類型。在C#中,它表示結構Interface Types接口Pointer Types指針Self-describing Types為垃圾回收器提供信息的數據類型(參見下一節(jié))Arrays包含對象數組的類型Class Types可自我描述的類型,但不是數組Delegates用于把引用包含在方法中的類型User-definedReference Types在源代碼中定義,且保存為引用類型的數據類型。在C#中,它表示類Boxed
27、 Value Types值類型,臨時打包放在一個引用中,以便于存儲在堆中這里沒有列出置的所有值類型,因為第3章將詳細介紹它們。在C#中,編譯器識別的每個預定義類型都映射為一個IL置類型。這與Visual Basic 2008是一樣的。(2) 公共語言規(guī)(CLS)公共語言規(guī)(Common Language Specification,CLS)和通用類型系統(tǒng)一起確保語言的互操作性。CLS是一個最低標準集,所有面向.NET的編譯器都必須支持它。因為IL是一種涵非常豐富的語言,大多數編譯器的編寫人員有可能把給定編譯器的功能限制為只支持IL和CLS提供的一部分特性。只要編譯器支持已在CLS中定義的容,這
28、就是很不錯的。提示:編寫非CLS兼容代碼是完全可以接受的,只是在編寫了這種代碼后,就不能保證編譯好的IL代碼完全支持語言的互操作性。下面的一個例子是有關區(qū)分大小寫字母的。IL是區(qū)分大小寫的語言。使用這些語言的開發(fā)人員常常利用區(qū)分大小寫所提供的靈活性來選擇變量名。但Visual Basic 2008是不區(qū)分大小寫的語言。CLS就要指定CLS兼容代碼不使用任何只根據大小寫來區(qū)分的名稱。因此,Visual Basic 2008代碼可以與CLS兼容代碼一起使用。這個例子說明了CLS的兩種工作方式。首先是各個編譯器的功能不必強大到支持.NET的所有功能,這將鼓勵人們?yōu)槠渌嫦?NET的編程語言開發(fā)編譯器
29、。第二,它提供如下保證:如果限制類只能使用CLS兼容的特性,就要保證用其他兼容語言編寫的代碼可以使用這個類。這種方法的優(yōu)點是使用CLS兼容特性的限制只適用于公受保護的類成員和公共類。在類的私有實現方式中,可以編寫非CLS代碼,因為其他程序集(托管代碼的單元,參見本章后面的容)中的代碼不能訪問這部分代碼。這里不深入討論CLS規(guī)。在一般情況下,CLS對C#代碼的影響不會太大,因為C#中的非CLS兼容特性非常少。2. 垃圾收集垃圾收集器用來在.NET中進行存管理,特別是它可以恢復正在運行中的應用程序需要的存。到目前為止,Windows平臺已經使用了兩種技術來釋放進程向系統(tǒng)動態(tài)請求的存:
30、完全以手工方式使應用程序代碼完成這些工作。 讓對象維護引用計數。讓應用程序代碼負責釋放存是低級高性能的語言使用的技術,例如C+。這種技術很有效,且可以讓資源在不需要時就釋放(一般情況下),但其最大的缺點是頻繁出現錯誤。請求存的代碼還必須顯式通知系統(tǒng)它什么時候不再需要該存。但這是很容易被遺漏的,從而導致存泄漏。盡管現代的開發(fā)環(huán)境提供了幫助檢測存泄漏的工具,但它們很難跟蹤錯誤,因為直到存已大量泄漏從而使Windows拒絕為進程提供資源時,它們才會發(fā)揮作用。到那個時候,由于對存的需求很大,會使整個計算機變得相當慢。維護引用計數是COM對象采用的一種技術,其方法是每個COM組件都保留一個計
31、數,記錄客戶機目前對它的引用數。當這個計數下降到0時,組件就會刪除自己,并釋放相應的存和資源。它帶來的問題是仍需要客戶機通知組件它們已經完成了存的使用。只要有一個客戶機沒有這么做,對象就仍駐留在存中。在某些方面,這是比C+存泄漏更為嚴重的問題,因為COM對象可能存在于它自己的進程中,從來不會被系統(tǒng)刪除(在C+存泄漏問題上,系統(tǒng)至少可以在進程中斷時釋放所有的存)。.NET運行庫采用的方法是垃圾收集器,這是一個程序,其目的是清理存,方法是所有動態(tài)請求的存都分配到堆上(所有的語言都是這樣處理的,但在.NET中,CLR維護它自己的托管堆,以供.NET應用程序使用),當.NET檢測到給定進程的托管堆已滿
32、,需要清理時,就調用垃圾收集器。垃圾收集器處理目前代碼中的所有變量,檢查對存儲在托管堆上的對象的引用,確定哪些對象可以從代碼中訪問- 即哪些對象有引用。沒有引用的對象就不能再從代碼中訪問,因而被刪除。Java就使用與此類似的垃圾收集系統(tǒng)。之所以在.NET中使用垃圾收集器,是因為中間語言已用來處理進程。其規(guī)則要求,第一,不能引用已有的對象,除非復制已有的引用。第二,中間語言是類型安全的語言。在這里,其含義是如果存在對對象的任何引用,該引用中就有足夠的信息來確定對象的類型。垃圾收集器機制不能和諸如非托管C+這樣的語言一起使用,因為C+允許指針自由地轉換數據類型。垃圾收集器的一個重要方面是它的不確定
33、性。換言之,不能保證什么時候會調用垃圾收集器:.NET運行庫決定需要它時,就可以調用它(除非明確調用垃圾收集器)。但可以重寫這個過程,在代碼中調用垃圾收集器。3. 安全性.NET很好地補足了Windows提供的安全機制,因為它提供的安全機制是基于代碼的安全性,而Windows僅提供了基于角色的安全性?;诮巧陌踩越⒃谶\行進程的賬戶的身份基礎上,換而言之,就是誰擁有和運行進程。另一方面,基于代碼的安全性建立在代碼實際執(zhí)行的任務和代碼的可信程度上。由于中間語言提供了強大的類型安全性,所以CLR可以在運行代碼前檢查它,以確定是否有需要的安全權限。.NET還提供了一種機制,可以在運行代碼前指定代
34、碼需要什么安全權限?;诖a的安全性非常重要,原因是它降低了運行來歷不明的代碼的風險(例如代碼是從Internet上下載來的)。即使代碼運行在管理員賬戶下,也有可能使用基于代碼的安全性,來確定這段代碼是否仍不能執(zhí)行管理員賬戶一般允許執(zhí)行的某些類型的操作,例如讀寫環(huán)境變量、讀寫注冊表或訪問.NET反射特性。安全問題詳見本書后面的第20章。4. 應用程序域應用程序域是.NET中的一個重要技術改進,它用于減少運行應用程序的系統(tǒng)開銷,這些應用程序需要與其他程序分離開來,但仍需要彼此通信。典型的例子是Web服務器應用程序,它需要同時響應許多瀏覽器請求。因此,要有許多組件實例同時響應這些同時運行的請求。在
35、.NET開發(fā)出來以前,可以讓這些實例共享同一個進程,但此時一個運行的實例就有可能導致整個的崩潰;也可以把這些實例孤立在不同的進程中,但這樣做會增加相關性能的系統(tǒng)開銷。到現在為止,孤立代碼的唯一方式是通過進程來實現的。在運行一個新的應用程序時,它會在一個進程環(huán)境運行。Windows通過地址空間把進程分隔開來。這樣,每個進程有4GB的虛擬存來存儲其數據和可執(zhí)行代碼(4GB對應于32位系統(tǒng),64位系統(tǒng)要用更多的存)。Windows利用額外的間接方式把這些虛擬存映射到物理存或磁盤空間的一個特殊區(qū)域中,每個進程都會有不同的映射,虛擬地址空間塊映射的物理存之間不能有重疊,這種情況如圖1-2所示。
36、;圖 1-2 在一般情況下,任何進程都只能通過指定虛擬存中的一個地址來訪問存-即進程不能直接訪問物理存,因此一個進程不可能訪問分配給另一個進程的存。這樣就可以確保任何執(zhí)行出錯的代碼不會損害其地址空間以外的數據(注意在Windows 95/98上,這些保護措施不像在Windows NT/2000/XP/2003/Vista上那樣強大,所以理論上存在應用程序因寫入不對應的存而導致Windows崩潰的可能性)。進程不僅是運行代碼的實例相互隔離的一種方式,在Windows NT/2000/XP/2003/Vista系統(tǒng)上,它們還可以構成分配了安全權限和許可的單元。每個進程都有自己的安全標識
37、,明確地表示Windows允許該進程可以執(zhí)行的操作。進程對確保安全有很大的幫助,而它們的一大缺點是性能。許多進程常常在一起工作,因此需要相互通信。一個常見的例子是進程調用一個COM組件,而該COM組件是可執(zhí)行的,因此需要在它自己的進程上運行。在COM中使用代理時也會發(fā)生類似的情況。因為進程不能共享任何存,所以必須使用一個復雜的編組過程在進程之間復制數據。這對性能有非常大的影響。如果需要使組件一起工作,但不希望性能有損失,唯一的方法是使用基于DLL的組件,讓所有的組件在同一個地址空間中運行- 其風險是執(zhí)行出錯的組件會影響其他組件。應用程序域是分離組件的一種方式,它不會導致因在進程之間傳送數據而產
38、生的性能問題。其方法是把任何一個進程分解到多個應用程序域中,每個應用程序域大致對應一個應用程序,執(zhí)行的每個線程都運行在一個具體的應用程序域中,如圖1-3所示。如果不同的可執(zhí)行文件都運行在同一個進程空間中,顯然它們就能輕松地共享數據,因為理論上它們可以直接訪問彼此的數據。雖然在理論上這是可以實現的,但是CLR會檢查每個正在運行的應用程序的代碼,以確保這些代碼不偏離它自己的數據區(qū)域,保證不發(fā)生直接訪問其他進程的數據的情況。這初看起來是不可能的,如何告訴程序要做什么工作,而又不真正運行它? 圖 1-3實際上,這么做通常是可能的,因為中間語言擁有強大的類型安全功能。在大多數情況下
39、,除非代碼明確使用不安全的特性,例如指針,否則它使用的數據類型可以確保存不會被錯誤地訪問。例如,.NET數組類型執(zhí)行邊界檢查,以禁止執(zhí)行超出邊界的數組操作。如果運行的應用程序的確需要與運行在不同應用程序域中的其他應用程序通信或共享數據,就必須調用.NET的遠程服務。被驗證不能訪問超出其應用程序域的數據(而不是通過明確的遠程機制)的代碼就是存類型安全的代碼,這種代碼與運行在同一個進程中但應用程序域不同的類型安全代碼一起運行是安全的。1.3.4 通過異常處理錯誤.NET Framework可以根據異常使用一樣的機制處理錯誤情況,這與Java和C+是一樣的。C+開發(fā)人員應注意到,由于IL
40、有非常強大的類型系統(tǒng),所以在IL中以C+的方式使用異常不會帶來相關的性能問題。另外,.NET和C#也支持finally塊,這是許多C+開發(fā)人員長久以來的愿望。第14章會詳細討論異常。簡要地說,代碼的某些領域被看作是異常處理程序例程,每個例程都能處理某種特殊的錯誤情況(例如,找不到文件,或拒絕執(zhí)行某些操作的許可)。這些條件可以定義得很寬或很窄。異常結構確保在發(fā)生錯誤情況時,執(zhí)行進程立即跳到最合適的異常處理程序例程上,處理錯誤情況。異常處理的結構還提供了一種方便的方式,當對象包含錯誤情況的準確信息時,該對象就可以傳送給錯誤處理例程。這個對象包括給用戶提供的相應信息和在代碼的什么地方檢測到錯誤的確切
41、信息。大多數異常處理結構,包括異常發(fā)生時的程序流控制,都是由高級語言處理的,例如C#、Visual Basic 2008和C+,任何中間語言中的命令都不支持它。例如,C#使用try、catch和 finally代碼塊來處理它,詳見第14章。.NET提供了一種基礎結構,讓面向.NET的編譯器支持異常處理。特別是它提供了一組.NET類來表示異常,語言的互操作性則允許異常處理代碼處理被拋出的異常對象,無論異常處理代碼使用什么語言編寫,都是這樣。語言的無關性沒有體現在C+和Java的異常處理中,但在COM的錯誤處理機制中有一定限度的體現。COM的錯誤處理機制包括從方法中返回錯誤代碼以與傳遞錯誤對象。在
42、不同的語言中,異常的處理是一致的,這是多語言開發(fā)的重要一環(huán)。1.3.5 特性的使用特性(attribute)是使用C+編寫COM組件的開發(fā)人員很熟悉的一個功能(在Microsoft的COM接口定義語言(Interface Definition Language,IDL)中使用特性)。特性最初是為了在程序中提供與某些項相關的額外信息,以供編譯器使用。.NET支持特性,因此現在C+、C#和Visual Basic 2008也支持特性。但在.NET中,對特性的革新是建立了一個機制,通過該機制可以在源代碼中定義自己的特性。這些用戶定義的特性將和對應數據類型或方法的元數據放在一起,這對于文檔
43、說明書十分有用,它們和反射技術一起使用,以根據特性執(zhí)行編程任務。另外,與.NET的語言無關性的基本原理一樣,特性也可以在一種語言的源代碼中定義,而被用另一種語言編寫的代碼讀取。本書的第13章詳細介紹了特性。1.4 程序集程序集(assembly)是包含編譯好的、面向.NET Framework的代碼的邏輯單元。本章不詳細論述程序集,而在第17章中論述,下面概述其中的要點。程序集是完全自我描述性的,也是一個邏輯單元而不是物理單元,它可以存儲在多個文件中(動態(tài)程序集的確存儲在存中,而不是存儲在文件中)。如果一個程序集存儲在多個文件中,其中就會有一個包含入口點的主文件,該文件描述了程序集
44、中的其他文件。注意可執(zhí)行代碼和庫代碼使用一樣的程序集結構。唯一的區(qū)別是可執(zhí)行的程序集包含一個主程序入口點,而庫程序集不包含。程序集的一個重要特性是它們包含的元數據描述了對應代碼中定義的類型和方法。程序集也包含描述程序集本身的元數據,這種程序集元數據包含在一個稱為"程序集清單"的區(qū)域中,可以檢查程序集的版本與其完整性。注意:ildasm是一個基于Windows的實用程序,可以用于檢查程序集的容,包括程序集清單和元數據。第17章將介紹ildasm。程序集包含程序的元數據,表示調用給定程序集中的代碼的應用程序或其他程序集不需要指定注冊表或其他數據源,以確定如何使用該程序集。這與以
45、前的COM有很大的區(qū)別,以前,組件的GUID和接口必須從注冊表中獲取,在某些情況下,方法和屬性的詳細信息也需要從類型庫中讀取。把數據分散在3個以上的不同位置上,可能會出現信息不同步的情況,從而妨礙其他軟件成功地使用該組件。有了程序集后,就不會發(fā)生這種情況,因為所有的元數據都與程序的可執(zhí)行指令存儲在一起。注意,即使程序集存儲在幾個文件中,數據也不會出現不同步的問題。這是因為包含程序集入口的文件也存儲了其他文件的細節(jié)、散列和容,如果一個文件被替換,或者被塞滿,系統(tǒng)肯定會檢測出來,并拒絕加載程序集。程序集有兩種類型:共享程序集和私有程序集。1.4.1 私有程序集私有程序集是最簡單的一種程
46、序集類型。私有程序集一般附帶在某個軟件上,且只能用于該軟件。附帶私有程序集的常見情況是,以可執(zhí)行文件或許多庫的方式提供應用程序,這些庫包含的代碼只能用于該應用程序。系統(tǒng)可以保證私有程序集不被其他軟件使用,因為應用程序只能加載位于主執(zhí)行文件所在文件夾或其子文件夾中的程序集。用戶一般會希望把商用軟件安裝在它自己的目錄下,這樣軟件包沒有覆蓋、修改或加載另一個軟件包的私有程序集的風險。私有程序集只能用于自己的軟件包,這樣,用戶對什么軟件使用它們就有了更多的控制。因此,不需要采取安全措施,因為這沒有其他商用軟件用某個新版本的程序集覆蓋原來的私有程序集的風險(但軟件是專門執(zhí)行懷有惡意的損害性操作的情況除外
47、)。名稱也不會有沖突。如果私有程序集中的類正巧與另一個人的私有程序集中的類同名,是不會有問題的,因為給定的應用程序只能使用私有程序集的名稱。因為私有程序集完全是自含式的,所以安裝它的過程就很簡單。只需把相應的文件放在文件系統(tǒng)的對應文件夾中即可(不需要注冊表項),這個過程稱為"0影響(xcopy)安裝"。1.4.2 共享程序集共享程序集是其他應用程序可以使用的公共庫。因為其他軟件可以訪問共享程序集,所以需要采取一定的保護措施來防止以下風險: 名稱沖突,另一個公司的共享程序集執(zhí)行的類型與自己的共享程序集中的類型同名。因為客戶機代碼理論上可以同時訪問這些程
48、序集,所以這是一個嚴重的問題。 程序集被同一個程序集的不同版本覆蓋- 新版本與某些已有的客戶機代碼不兼容。這些問題的解決方法是把共享程序集放在文件系統(tǒng)的一個特定的子目錄樹中,稱為全局程序集高速緩存(GAC)。與私有程序集不同,不能簡單地把共享程序集復制到對應的文件夾中,而需要專門安裝到高速緩存中,這個過程可以用許多.NET工具來完成,其中包含對程序集的檢查、在程序集高速緩存中設置一個小的文件夾層次結構,以確保程序集的完整性。為了避免名稱沖突,共享程序集應根據私鑰加密法指定一個名稱(私有程序集只需要指定與其主文件名一樣的名稱即可)。該名稱稱為強名(strong name),并保證其唯
49、一性,它必須由要引用共享程序集的應用程序來引用。與覆蓋程序集相關的問題,可以通過在程序集清單中指定版本信息來解決,也可以通過同時安裝來解決。1.4.3 反射因為程序集存儲了元數據,包括在程序集中定義的所有類型和這些類型的成員的細節(jié),所以可以編程訪問這些元數據。這個技術稱為反射,第13章詳細介紹了它們。該技術很有趣,因為它表示托管代碼實際上可以檢查其他托管代碼,甚至檢查它自己,以確定該代碼的信息。它們常常用于獲取特性的詳細信息,也可以把反射用于其他目的,例如作為實例化類或調用方法的一種間接方式,如果把方法上的類名指定為字符串,就可以選擇類來實例化方法,以便在運行時調用,而不是在編譯時
50、調用,例如根據用戶的輸入來調用(動態(tài)綁定)。1.5 .NET Framework類至少從開發(fā)人員的角度來看,編寫托管代碼的最大好處是可以使用.NET基類庫。.NET基類是一個容豐富的托管代碼類集合,它可以完成以前要通過Windows API來完成的絕大多數任務。這些類派生自與中間語言一樣的對象模型,也基于單一繼承性。無論.NET基類是否合適,都可以實例化對象,也可以從它們派生自己的類。.NET基類的一個優(yōu)點是它們非常直觀和易用。例如,要啟動一個線程,可以調用Thread類的Start()方法。要禁用TextBox,應把TextBox對象的Enabled屬性設置為false。Visu
51、al Basic和Java開發(fā)人員非常熟悉這種方式。它們的庫都很容易使用,但對于C+開發(fā)人員來說這是極大的解脫,因為他們多年來一直在使用諸如GetDIBits()、RegisterWndClassEx()和IsEqualIID()這樣的API函數,以與需要傳遞Windows句柄的函數。另一方面,C+開發(fā)人員總是很容易訪問整個Windows API,而Visual Basic 6和Java開發(fā)人員只能訪問其語言所能訪問的基本操作系統(tǒng)功能。.NET基類的新增容就是把Visual Basic和Java庫的易用性和Windows API函數的豐富功能結合起來。但Windows仍有許多功能不能通過基類來
52、使用,而需要調用API函數。但一般情況下,這只限于比較復雜的特性?;悗熳阋詰度粘9ぷ鞯氖褂谩H绻枰{用API函數,.NET提供了所謂的"平臺調用",來確保對數據類型進行正確的轉換,這樣無論是使用C#、C+或Visual Basic 2008進行編碼,該任務都不會比直接從已有的C+代碼中調用函數更困難。注意:WinCV是一個基于Windows的實用程序,可以用于瀏覽基類庫中的類、結構、接口和枚舉。本書將在第15章介紹WinCV。第3章主要介紹基類。完成了C#語言語法的概述后,本書的其余容將主要說明如何使用.NET Framework 3.5的.NET基類庫中的各種類,即
53、各種基類是如何工作的。.NET 3.5基類包括: IL提供的核心功能,例如,通用類型系統(tǒng)中的基本數據類型,詳見第3章。 Windows GUI支持和控件(第31和34章) Web窗體(ASP.NET,第37和38章) 數據訪問(ADO.NET,第2630章) 目錄訪問(第46章) 文件系統(tǒng)和注冊表訪問(第25章) 網絡和Web瀏覽(第41章) .NET特性和反射(第13章) 訪問Windows操作系統(tǒng)的各個方面(例如環(huán)境變量等,第20章) COM互操作性(第44和24章)附帶
54、說一下,根據Microsoft源文件,大部分.NET基類實際上都是用C#編寫的!1.6 命名空間命名空間是.NET避免類名沖突的一種方式。例如,命名空間可以避免下述情況:定義一個類來表示一個顧客,稱此類為Customer,同時其他人也在做一樣的事(這有一個類似的場景- 顧客有相當多的業(yè)務)。命名空間不過是數據類型的一種組合方式,但命名空間中所有數據類型的名稱都會自動加上該命名空間的名字作為其前綴。命名空間還可以相互嵌套。例如,大多數用于一般目的的.NET基類位于命名空間System中,基類Array在這個命名空間中,所以其全名是System.Array。.NET需要在命名空間中定義
55、所有的類型,例如,可以把Customer類放在命名空間YourCompanyName中,則這個類的全名就是YourCompanyName.Customer。注意:如果沒有顯式提供命名空間,類型就添加到一個沒有名稱的全局命名空間中。Microsoft建議在大多數情況下,都至少要提供兩個嵌套的命名空間名,第一個是公司名,第二個是技術名稱或軟件包的名稱,而類是其中的一個成員,例如YourCompanyName.Sales Services.Customer。在大多數情況下,這么做可以保證類的名稱不會與其他組織編寫的類名沖突。第2章將詳細介紹命名空間。1.7 用C#創(chuàng)建.NET應用程序C#
56、可以用于創(chuàng)建控制臺應用程序:僅使用文本、運行在DOS窗口中的應用程序。在進行單元測試類庫、創(chuàng)建Unix/Linux daemon進程時,就要使用控制臺應用程序。但是,我們常常使用C#創(chuàng)建利用許多與.NET相關的技術的應用程序,下面簡要論述可以用C#創(chuàng)建的不同類型的應用程序。1.7.1 創(chuàng)建ASP.NET應用程序ASP是用于創(chuàng)建帶有動態(tài)容的Web頁面的一種Microsoft技術。ASP頁面基本是一個嵌有服務器端VBScript或JavaScript代碼塊的HTML文件。當客戶瀏覽器請求一個ASP頁面時,Web服務器就會發(fā)送頁面的HTML部分,并處理服務器端腳本。這些腳本通常會查詢數據
57、庫的數據,在HTML中標記數據。ASP是客戶建立基于瀏覽器的應用程序的一種便利方式。但ASP也有缺點。首先,ASP頁面有時顯示得比較慢,因為服務器端代碼是解釋性的,而不是編譯的。第二,ASP文件很難維護,因為它不是結構化的,服務器端的ASP代碼和一般的HTML會混合在一起。第三,ASP有時開發(fā)起來會比較困難,因為它不支持錯誤處理和類型檢查。特別是如果使用VBScript,并希望在頁面中進行錯誤處理,就必須使用On Error Resume Next語句,通過Err.Number檢查每個組件調用,以確保該調用正常進行。ASP.NET是ASP的全新修訂版本,它解決了ASP的許多問題。但ASP.NE
58、T頁面并沒有替代ASP,而是可以與原來的ASP應用程序在同一個服務器上并存。當然,也可以用C#編寫ASP.NET。后面的章節(jié)(第37、38和39章)會詳細討論ASP.NET,這里僅解釋它的一些重要特性。1. ASP.NET的特性首先,也是最重要的是,ASP.NET頁面是結構化的。這就是說,每個頁面都是一個繼承了.NET類System.Web.UI.Page的類,可以重寫在Page對象的生存期中調用的一系列方法,(可以把這些事件看成是頁面所特有的,對應于原ASP的global.asa文件中的OnApplication_Start 和OnSession_Start事件)。因為可以把一個頁面的功能放
59、在有明確含義的事件處理程序中,所以ASP.NET比較容易理解。ASP.NET頁面的另一個優(yōu)點是可以在Visual Studio 2008中創(chuàng)建它們,在該環(huán)境下,可以創(chuàng)建ASP.NET頁面使用的業(yè)務邏輯和數據訪問組件。Visual Studio 2008項目(也稱為解決方案)包含了與應用程序相關的所有文件。而且,也可以在編輯器中調試傳統(tǒng)的ASP頁面,在以前使用Visual InterDev時,把InterDev和項目的Web服務器配置為支持調試常常是一個讓人頭痛的問題。最清楚的是,ASP.NET的后臺編碼功能允許進一步采用結構化的方式。ASP.NET允許把頁面的服務器端功能單獨放在一個類中,把該類編譯為DLL,并把該DLL放在HTML部分下面的一個目錄中。放在頁面頂部的后臺編碼指令將把該文件與其DLL關聯起來。當瀏覽器請求該頁面時,Web服務器就會在頁面的后臺DLL中引發(fā)類中的事件。最后,
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 倉儲設備租賃合同協議書
- 人工智能技術應用研發(fā)合作協議
- 鋼筋焊接施工承包合同
- 工程承包合同單價合同
- 企業(yè)信息化戰(zhàn)略規(guī)劃與實施
- 工廠場地租賃合同
- 電子商務購銷合同
- 數據安全與信息保密服務協議
- 血液(第二課時)課件2024-2025學年北師大版生物七年級下冊
- 關于調整辦公環(huán)境的申請通知
- 部編版六年級下冊數學教學計劃(及進度表)
- 會計學生學情分析總結
- 大學英語六級考試
- 新質生產力:中國創(chuàng)新發(fā)展的著力點與內在邏輯
- 中考數學第二輪復習教案
- (2024年)職業(yè)健康培訓課件(PPT9)
- 心理健康與職業(yè)生涯(中等職業(yè))全套教學課件
- 黑龍江農業(yè)經濟職業(yè)學院單招《語文》考試復習題庫(含答案)
- 人工智能在物業(yè)管理中的應用
- 基于BIM的軸流通風機施工工藝優(yōu)化
- 在醫(yī)院新員工入職儀式上的講話
評論
0/150
提交評論