.NET系統框架

本書是一本講解.NET技術的書籍,目標讀者羣也是在.NET框架(.NET Framework)下進行開發的程序員,所以咱們沒法迴避的問題就是:什麼是.NET框架?它包含了哪些內容?爲開發程序提供了哪些支持?不少朋友對這類個問題的第一反應多是.NET框架所提供的龐大類庫及編寫代碼所採用的C#語言,實際上遠不止這些。html

要描述.NET框架,天然會遇到與其相關的一系列專業的技術術語和縮寫,相信你們已經見到過許多了,好比:CLI、CIL、CTS、CLS、CLR、JIT、BCL、FCL、Module、Assembly 等,足以讓不少人一頭霧水、望而卻步。筆者不會像字典同樣按首字母排序對術語進行逐一解釋,由於這樣仍是難以理解。咱們仍是從你們最熟悉的東西開始吧!程序員

6.1 引子

設想一下:編寫下面這樣一個最簡單的顯示「Hello, World!」的控制檯程序,並將該程序運行起來須要哪幾個步驟呢?編程

using System;
 
class Program {
    static void Main(string[] args) {
        string text = "hello, world!";
        Console.WriteLine(text);
    }
}瀏覽器

這些步驟包括:打開Visual Studio,建立一個C#控制檯應用程序項目(在這裏將它命名爲ConsoleApp),編寫代碼,編譯程序而後運行。雖然這樣的程序誰都會寫,可是再多進行一下思考就會發現,儘管是一個很小的程序,但已經引入了.NET框架的幾個重要方面。緩存

若是建立一個VB.NET類型的項目,實現和上面C#項目徹底同樣的功能,那麼編譯後生成的文件有什麼區別?sass

編寫控制檯應用程序,將字符輸出到屏幕,須要調用Console.WriteLine()方法。這個Console類型從何而來呢?安全

生成的文件在系統中是如何運行起來的?其機制和使用傳統VC++生成的可執行文件是否相同?服務器

其實,上面每個問題的答案都包含.NET框架所提供的支持,這裏將它分爲三個部分:網絡

  • 對於編譯後生成的文件格式和內容,.NET中存在着諸多規範。符合這些規範的程序語言,也叫作面向.NET的語言。編譯後生成的文件均可以在.NET運行時下執行,這就是你們所熟知的.NET多語言支持。
  • 在開發階段,.NET提供了一個龐大的類庫,支持開發者快速開發各類應用程序,也支持程序語言設計者開發其語言編譯器。
  • 在程序執行階段,.NET提供了一個程序運行時的環境,這個運行時環境幫助咱們管理內存、實時編譯程序、進行安全檢查、執行垃圾回收等。

接下來就針對上述內容開始爲你們詳細講述。數據結構

6.2 CIL——公共中間語言

首先要了解的就是C#程序源碼在編譯以後會獲得什麼樣的一個文件。你們知道,過去使用VC++生成的可執行文件,通過預編譯、編譯、彙編、連接幾個步驟後,最終生成的可執行文件中就已經包含了處理器的本地代碼(Native Code),支持它運行的只是操做系統和本地的機器指令集。那麼採用C#編譯器生成的文件又是什麼呢?如今須要引入程序集這個概念:在.NET框架下,相似C#這樣的高級語言通過編譯後生成的結果文件被稱作程序集,其後綴名是.dll(類庫)或.exe(可執行程序)。在引入這個概念以前,前面(上一節)提到程序集時,都是用「文件」這個詞來描述的。

程序集的定義只是給編譯後生成的文件一個稍微正式一點的名稱,對於解釋「它是由什麼構成的」這個問題並無太大的幫助。爲了進一步瞭解程序集,咱們再來作一個試驗,使用VB.NET建立一個控制檯應用程序項目(ConsoleAppVB),並生成一個程序集,代碼功能和上面用C#建立的項目是同樣的的。

Module Program
Sub Main()
Dim text AsString = "hello, world !"
        Console.WriteLine(text)
EndSub
EndModule

如今,須要一個工具來查看這個程序集的內容,而且與C#項目生成的程序集進行對比。還好,微軟已經提供了一個利器——IL DASM(IL Disassembler,IL反彙編程序)來幫助開發者查看程序集的信息。若是安裝了Visual Studio,IL DASM將會隨同Visual Studio一塊兒安裝。依次選擇開始菜單→ Microsoft Visual Studio 2010 → Microsoft Windows SDK Tools →IL 反彙編程序(IL DASM)能夠啓動IL DASM。

打開IL DASM後選擇VB.NET項目生成的ConsoleAppVB.exe,能夠看到如圖6-1所示的界面。

圖6-1 IL DASM 運行界面

這部份內容不少,會在下一章「程序集」中進行專門講述,,這裏暫且略過。展開圖6-1中的ConsoleAppVB.Program類型,在Main()方法上雙擊,會彈出另一個窗口,顯示圖6-2中的代碼,看上去有點像彙編語言。在這裏能夠看到熟悉的string text變量聲明及「hello, world !」。

圖6-2 方法體的CIL語言描述(VB.NET)

接下來再打開C#項目生成的ConsoleApp.exe,進行一樣的操做,在打開Main()方法後會發現其中的代碼與圖6-2中幾乎徹底同樣,如圖6-3所示

圖6-3方法體的CIL語言描述(C#)

至此,能夠獲得一個初步的推斷:無論是VB.NET仍是是C#,編譯以後的程序集都可以用IL DASM打開,所以它們生成的程序集的格式都是相同的;當程序所實現的功能相同時,程序集所包含的CIL代碼也是相似的。

如今對上面程序集中所包含的相似彙編的語言作一下介紹,便是本節標題中的CIL(Common Intermediate Language,公共中間語言)。CIL最初是隨着.NET由微軟一塊兒發佈的,所以以前也叫作MSIL(Microsoft Intermediate Language),後來進行了標準化,以後便被稱作CIL。在一些書或文章中,CIL也會簡寫爲IL,其實都是指一樣的東西。爲了不混淆,本書統一用CIL這個縮寫。

咱們能夠將上面的過程用圖6-4來表示出來。

圖6-4 源程序編譯爲了程序集

接下來再深刻地分析一下,公共中間語言這個術語到底包含了哪幾層含義。

  • 公共。由於不管是C#語言也好,VB.NET語言也好,C++/CLI語言也好,甚至是從新開發的一套以本身的名字縮寫命名的語言,只要它指望運行的目標平臺是.NET,在通過相應的編譯器編譯以後,所生成的程序集就是由CIL語言代碼描述的。
  • 中間。這個詞也是大有深意,爲何不叫公共機器語言(Common Machine Language),或者公共本地語言(Common Native Language)?由於這種語言只是比咱們使用的高級語言,好比C#低級一點,並非CPU能夠直接執行的本地機器語言。這種語言還須要.NET運行時(.Net runtime)環境的支持,在執行以前,進行一個被稱爲Just-in-time(即時)的二次編譯過程,才能轉變成計算機能夠識別的指令。關於.NET運行時,以及詳細過程後面再介紹,如今只要知道,這個文件所包含的CIL代碼並不是機器能夠直接執行的指令代碼。
  • 語言。CIL不過是一種程序語言,只不過相對於C#來講,它是一種更低級語言。從圖6-2 的代碼截圖中,已經能夠看到,CIL是一種基於堆棧的語言,同時,它提供了class、interface、繼承、多態等諸多面向對象的語言特性,所以它又是徹底面向對象的語言。若是願意,甚至能夠直接編寫CIL代碼,而且使用CIL的編譯工具IL ASM(IL Assembler,IL彙編程序)來對它進行編譯。只不過,和大多數低級語言同樣,這種方式會使開發效率會變得很低。這裏注意區別一下IL ASM和IL DASM,它們的拼寫是不一樣的。

爲了加深一下印象,咱們來作一個試驗:編寫一段簡單的CIL代碼,而且使用IL ASM工具對其進行編譯,獲得和前面同樣的ConsoleApp.exe程序。

1)打開記事本程序,輸入下面的代碼,而後將其保存在D:\ConsoleApp.il。

.assembly extern mscorlib{}
.assembly ConsoleApp{}
.module ConsoleApp.exe
.class public auto ansi Program extends System.Object
{
    .method public static void Main()
    {
        .entrypoint
        nop
        ldstr "Hello, World!"
        call void [mscorlib]System.Console::WriteLine(string)
        nop
        ret
    }
}

2)打開Visual Studio 2010命令行工具,輸入:

D:\>ilasm ConsoleApp.il

3)成功後會看到ConsoleApp.exe程序,它的執行結果和上面用C#編寫的徹底同樣。

因爲程序集是由CIL語言所描述的,所以CIL也叫作程序集語言(Assembly Language)。又由於.NET程序集須要由.NET運行時加載才能運行,能夠視其爲由.NET運行時進行管理的,因此CIL代碼也叫作託管代碼(Managed Code)。相對的,不須要.NET運行時就能夠執行的代碼就叫作非託管代碼(Unmanaged Code)。

好了,已經知道了CIL的存在,從如今開始,最好在頭腦裏創建起兩個模型或兩種視角:一種是基於C#或其餘高級語言的源程序的視角,一種是基於CIL中間語言的程序集視角。C#源程序在被編譯爲程序集之後,就獨立於C#,所以程序集能夠由其餘種類的語言所調用;同時,由於程序集並無包含本地機器的指令,因此它與具體的機器類型也分隔開了,能夠被裝有.NET框架的任何機器運行。

6.3 BCL和FCL

6.3.1 BCL——基類庫

咱們先來看一個有意思的現象:再次打開前面建立的C#控制檯項目(ConsoleApp),而後在解決方案面板下打開「引用」文件夾,若是用的是Visual Studio 2010,而且面向的目標框架是.NET 4.0版本,那麼將會看到如圖6-5所示的這些引用。

圖6-5 解決方案中的「引用」文件夾

在建立項目時並無作任何額外的操做,那麼這些引用顯然是在建立項目時自動添加的。爲了方便初學者,這裏稍微解釋一下:要使用(實際上筆者以爲Consume這個詞表達的更貼切)其餘開發者所設計的類型,就須要在項目中將該類型所在的程序集引用進來。如今看到的這些程序集引用,都是微軟認爲很經常使用的,幾乎是每一個項目都會使用到的,因此在建立項目時自動添加了進來,省得開發者再手動進行添加。

可是在這裏這些引用不利於咱們理解一些內容,因此咱們把這些引用所有刪除掉,如圖6-6所示,而後再次編譯程序。

圖6-6 刪除掉全部的項目引用

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace ConsoleApp {
    classProgram {
        staticvoid Main(string[] args) {
            string text = "Hello, world!";
            Console.WriteLine(text);
        }
    }
}

可能有人會認爲,在刪掉這些引用以後,編譯器將會絕不客氣地提示編譯錯誤:未能找到類型或命名空間「System」(是否缺乏using指令或程序集引用?)。可實際上,當編譯並運行上面的代碼時,程序會正確無誤地執行。這是由於咱們已經刪掉了全部引用的程序集,只定義了一個Program類型,並無定義Console類型,因此此時要面對的第一個問題就是:Console類型從哪裏來?

Visual Studio提供了一個快捷的辦法使咱們能夠快速查看類型:將光標定位在Console上,而後按下鍵盤上的F12,就能夠看到Console的類型定義。在Console類型定義的最上方,能夠看到它所在的程序集地址:C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\mscorlib.dll。

#region 程序集 mscorlib.dll, v4.0.30319
// C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\mscorlib.dll
#endregion
using System.IO;
using System.Runtime.ConstrainedExecution;
using System.Security;
using System.Text;
 
namespace System {
    public static class Console {
        // 中間略
    }
}

能夠看到Console類型來自於mscorlib.dll這個程序集。從上面的實驗能夠看出,無論咱們是否引用mscorlib.dll程序集,它老是會自動引用進來。這個程序集中所包含的類庫,便是本節標題中的BCL(Base Class Library,基類庫)。從名字就能夠看出來,這個類庫包含的都是些最基本的類型,其自己已經與CIL語言融爲一提了,爲CIL語言提供基礎的編程支持,以致於該類庫已經成爲了CLI標準的一部分(後面會介紹,所以也能夠說BCL中的類型就是CIL語言的類型,全部面向CIL的語言都可以使用它們。咱們可使用對象瀏覽器(Visual Studio菜單→視圖→對象瀏覽器)來查看mscorlib.dll程序集中都包含了哪些命名空間和類型,如圖6-7所示。

圖6-7 mscorlib.dll中包含的命名空間

能夠看到該程序集下包含的主要是System命名空間,稍微細心一點的讀者會發現,在新建項目的時候,還包含了System.dll程序集,而且其中所包含的類型與mscorlib中的類型十分類似。

圖6-8 System 程序集

圖6-9 System.dll中包含的命名空間

這又是怎麼回事呢?實際上,只要點開System命名空間就會發現,mscorlib.dll的System命名空間下面定義的類型和System.dll的System命名空間下面定義的類型徹底不一樣,它們之間並無衝突之處。

如今就明白了:BCL提供了像Console這樣的類型來支持開發者編寫相似控制檯這樣的程序。

既然已經思考了這麼多,不妨再深刻一下,思考這樣一個問題:寫下的這條語句string text = 「hello, world !」,其中的string從哪裏來?從直覺來看,string在Visual Studio中以深藍色呈現,屬於C#的關鍵字,那麼它應該是C#提供的內置類型。但是,當咱們將光標移動到string上並按下F12時,轉到string的定義時,看到的倒是下面這樣的內容:

#region 程序集 mscorlib.dll, v4.0.30319
// C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\mscorlib.dll
#endregion
 
using System.Collections;
using System.Collections.Generic;
// 爲了節約篇幅,省略了一些using
 
namespace System {
    public sealed class String : IComparable, ICloneable, IConvertible, IComparable<string>, IEnumerable<char>, IEnumerable, IEquatable<string> {
    // 省略定義
    }
}

注意最上方的程序集地址,再次看到了mscorlib.dll,而且String類型與Console類型同樣,同位於System命名空間下。因而可知,C#的關鍵字string,不過是BCL中System.String類型的一個別名而已。相似地,VB.NET中的String關鍵字也是BCL中的System.String類型的別名。所以,在.NET框架中,語言從本質上來講沒有太大的區別,更多的區別是在語法方面。從上面的例子也能夠看出,C#和VB.NET的不少語言能力並非本身的,而是從CIL「借」過來的這樣作也保證了在不一樣語言中相應類型的行爲是一致的。

表6-1列出了幾個典型的,不一樣語言關鍵字與CIL類型的對應關係。筆者以爲理解重於記憶,因此這裏只列出了幾個。要了解其餘基礎類型時,只要將光標移動到類型上,而後再按下F12鍵就能夠了。

表6-1不一樣語言關鍵字與CIL類型的對應關係

CIL 類型 C# 關鍵字 VB.NET關鍵字
System.Byte byte Byte
Sytem.Int16 short Short
System.Int64 long Long

從表6-1能夠看出,.NET同時也對語言開發者提供支持.如你須要設計一款語言,那麼在開發編譯器時將語言的關鍵字映射爲CIL中的類型就能夠了,也就是說,對本身語言中的一些特殊符號(關鍵字)進行映射處理,就好像C#中的關鍵字int和string同樣。

你們可能據說過這樣一種特殊的類型——基元類型(Primitive Type)。實際上,講到這裏你們應該已經明白了,那些由編譯器直接支持,將語言自己的關鍵字類型轉換爲CIL類型的,就叫作基元類型。顯然,上面的byte、int、string都是基元類型。而C#中並無一個關鍵字去映射Console,因此咱們認爲Console只是普通的類類型(Class Type)。

6.3.2 FCL——框架類庫

做爲一名.NET程序員,天天都要打交道的就是FCL了(Framework Class Library,框架類庫)。在上一節中介紹了BCL,它是FCL的一個子集。BCL中包含了與編譯器及CIL語言關係緊密的核心類型,以及常見開發任務中都會使用到的類型。而FCL包含的內容極多,僅服務於一種應用場景的子類庫就足夠寫一本書了,這裏僅簡單對它進行介紹。

從功能上來看,能夠將FCL框架類庫劃分紅如下幾層。

  • 最內一層,由BCL的大部分組成,主要做用是對.NET框架、.NET運行時及CIL語言自己進行支持,例如基元類型、集合類型、線程處理、應用程序域、運行時、安全性、互操做等。
  • 中間一層,包含了對操做系統功能的封裝,例如文件系統、網絡鏈接、圖形圖像、XML操做等。
  • 最外一層,包含各類類型的應用程序,例如Windows Forms、Asp.NET、WPF、WCF、WF等。

6.4 CTS——公共類型系統

假設要開發一套新的語言,這種語言和C#或VB.NET同樣,在編譯後也可以生成CIL代碼,也能夠在.NET環境下運行,那麼首先須要什麼呢?

根據6.2節所講述的內容咱們知道,要開發的新語言至關於CIL的高級語言版本,因此實際上要作什麼並非由新語言決定的,而是由CIL來決定的。所以,須要一套CIL的定義、規則或標準。這套規則定義了咱們的語言能夠作什麼,不能夠作什麼,具備哪些特性。這套規則就稱做CTS(Common Type System,公共類型系統)。任何知足了這套規則的高級語言就能夠稱爲面向.NET框架的語言。C#和VB.NET不過是微軟本身開發的一套符合了CTS的語言,實際上還有不少的組織或團體,也開發出了這樣的語言,好比Delphi.Net、FORTRAN等。

那麼CTS具體包括哪些內容呢?在回答這個問題以前咱們須要弄清楚一個概念。仍是經過一段C#代碼來講明,先看下面幾行代碼:

public class Book { 
// 省略實現
}
Book item1 = new Book();
Book item2 = new Book();

對於以上代碼,一般是這麼描述的:定義了一個Book類,而且建立了兩個Book類的實例item一、item2。實際上這隻包含了兩層含義如表6-2所示。

表6-2 類、類的實例

Book
類的實例 item1,item2

再思考一下就會發現,還有一個更高的層面,那就是Book這個類的類型,咱們稱之爲類類型(Class Type),所以上表能夠改爲如表6-3所示。

表6-3 類類型、類、類的實例

類類型 class
Book
類的實例 item1,item2

相似的,還有枚舉類型(Enum Type)、結構類型((Struct Type)等。如今你們應該明白這裏要表達的意思了,CTS規定了能夠在語言中定義諸如類、結構、委託等類型,這些規則定義了語言中更高層次的內容。所以,在C#這個具體的語言實現中,咱們才能夠去定義類類型(Class Type)或者結構類型(Struct Type)等。

一樣,能夠在Book類中定義一個字段name並提供一個方法ShowName()。實際上,這些也是CTS定義的,它規範了類型中能夠包含字段(filed)、屬性(property)、方法(method)、事件(event)等。

除了定義各類類型外,CTS還規定了各類訪問性,好比Private、Public、Family(C#中爲Protected)、Assembly(C#中爲internal)、Family and assembly(C#中沒有提供實現)、Family or assembly(C#中爲protected internal)。

CTS還定義了一些約束,例如,全部類型都隱式地繼承自System.Object類型,全部類型都只能繼承自一個基類。從CTS的名稱和公共類型系統能夠看出,不只C#語言要知足這些約束,全部面向.NET的語言都須要知足這些約束。衆所周知,傳統C++是能夠繼承自多個基類的。爲了讓熟悉C++語言的開發者也能在.NET框架上開發應用程序,微軟推出了面向.NET的C++/CLI語言(也叫託管C++),它就是符合CTS的C++改版語言,爲了知足CTS規範,它被限制爲了只能繼承自一個基類。

關於上面內容有兩點須要特別說明:

1)C#並無提供Family and assembly的實現,C#中也沒有全局方法(Global Method)。換言之,C#只實現了CTS 的一部分功能。,也就是說,CTS規範了語言可以實現的全部能力,可是符合CTS規範的具體語言實現不必定要實現CTS規範所定義的所有功能。

2)C++/CLI又被約束爲只能繼承自一個基類,換言之,C++中的部分功能被刪除了。,就是說,任何語言要符合CTS,其中與CTS不兼容的部分功能都要被捨棄。

顯然,因爲CIL是.NET運行時所能理解的語言,所以它實現了CTS的所有功能。雖然它是一種低級語言,可是實際上,它所具備的功能更加完整。C#語言和CIL的關係,能夠用圖6-10進行表示。

圖6-10 C#和CIL的關係

6.5 CLS——公共語言規範

既然已經理解了CTS是一套語言的規則定義,就能夠開發一套語言來符合CTS了。假設這個語言叫作N#,它所實現的CTS很是有限,僅實現了其中不多的一部分功能,它與CTS和C#語言的關係可能如圖6-11所示。

圖6-11 C#、N#和CIL的關係

那麼如今就有一個問題:由C#編寫的程序集,可以引用由N#編寫的程序集嗎?答案顯然是不能,,雖然C#和N#同屬於CTS旗下,可是它們並無共通之處。所以,雖然單獨的N#或C#程序能夠完美地在.NET框架下運行,可是它們之間卻沒法相互引用。若是使用N#開發項目的開發者原本就不但願其餘語言類型的項目來引用他的項目倒也罷了,可是,若是N#項目指望其餘語言類型的項目可以對它進行引用,就須要N#中公開的類型和功能知足C#語言的特性,即它們須要有共通之處。注意,這句話中有一個詞很重要,就是「公開的」(public)。N#中不公開的部分(private、internal、protected)是不受影響的,可使用獨有的語言特性,由於這些不公開的部分原本就不容許外部進行訪問。所以, 若是N#想要被C#所理解和引用,它公開的部分就要知足C#的一些規範,此時,它與CTS和C#語言的關係就會變成如圖6-12所示。

圖6-12 C#、N#、CIL的關係

若是世界上僅有C#和N#兩種語言就好辦了,把它們共同的語言特性提取出來,而後要求全部公開的類型都知足這些語言特性,這樣C#和N#程序集就能夠相互引用了。可問題是:語言類型有上百種之多,而且.NET的設計目標是實現一個開放的平臺,不只現有的語言通過簡單修改就能夠運行在.NET框架上,後續開發的新語言也能夠,而新語言此時並不存在,如何提取出它的語言特性?所以又須要一套規範和標準來定義一些常見的、大多數語言都共有的語言特性。對於將來的新語言,只要它公開的部分可以知足這些規範,就可以被其餘語言的程序集所使用。這個規範就叫作CLS (Common Language Specification,公共語言規範)。很明顯,CLS是CTS的一個子集。如今引入了CLS,圖6-12的關係圖就能夠改爲如圖6-13所示。

圖6-13 語言、CLS、CIL的關係

若是利用C#開發的一個程序集的公開部分僅採用了CLS中的特性,那麼這個程序集就叫作CLS兼容程序集(CLScompliant assembly)。顯然,對於上面提到的FCL框架類庫,其中的類型都符合CLS,僅有極個別類型的成員不符合CLS,這就保證了全部面向.NET的語言均可以使用框架類庫中的類型。

如今,讀者又會有一個疑問:上面幾段文字中反覆出現了一個詞———「語言特性」(language features),知足CLS就是要求語言特性要一致,那麼什麼叫作語言特性?這裏給出幾個具體的語言特性:是否區分大小寫,標識符的命名規則如何,可使用的基本類型有哪些,構造函數的調用方式(是否會調用基類構造函數),支持的訪問修飾符等。

那麼咱們如何檢驗程序集是否符合CLS呢?.NET爲咱們提供了一個特性CLSCompliant,便於在編譯時檢查程序集是否符合CLS。咱們來看下面一個例子:

using System;
 
[assembly:CLSCompliant(true)]
 
public class CLSTest {
 
    public string name;
 
    // 警告:僅大小寫不一樣的標識符「CLSTest.Name()」不符合 CLS
    public string Name() {
        return "";
    }
 
    // 警告:「CLSTest.GetValue()」的返回類型不符合 CLS
    public uint GetValue() {
        return 0;
    }
 
    // 警告: 參數類型「sbyte」不符合 CLS
    public void SetValue(sbyte a) { }
 
    // 警告標識符「CLSTest._aFiled」不符合 CLS
    public string _MyProperty { get; set; }
}

能夠注意到,在CLSTest類的前面爲程序集加上了一個CLSCompliant特性,代表這個程序集是CLS兼容的。可是,有三處並不知足這個要求,所以編譯器給出了警告信息。這三處是:

  • 不能以大小寫來區分紅員,所以字段name和方法Name()不符合CLS。
  • 方法的返回類型和參數類型必須是CLS兼容的,uint和sbyte類型並不是CLS兼容,所以GetValue()和SetValue()方法不符合CLS。
  • 標識符的命名不能如下劃線「_」開頭,所以屬性_MyProperty不符合CLS。

還會注意到,編譯器給出的只是警告信息,而非錯誤信息,所以能夠無視編譯器的警告,不過這個程序集只能由其餘C#語言編寫的程序集所使用。

6.6 CLR——公共語言運行時

6.6.1 程序集概述

前面提到過:程序集包含了CIL語言代碼,而CIL語言代碼是沒法直接運行的,須要通過.NET運行時進行即時編譯才能轉換爲計算機能夠直接執行的機器指令。那麼這個過程是如何進行的呢?

接下來咱們要了解的就是.NET框架的核心部分:CLR(Common Language Runtime),公共語言運行時),有時也會稱作.NET運行時(.NET runtime)。在瞭解CLR以前,須要先進一步學習一下程序集,由於下一節會對程序集進行專門的講述,這裏僅簡單介紹一下程序集中對於理解CLR有幫助的概念。

從直覺上來看,前面以.exe爲後綴的控制檯應用程序就是一個直接的可執行文件,由於在雙擊它後,它確實會運行起來。這裏的狀況和麪向對象中的繼承有一點像:一臺轎車首先是一部機動車、一隻貓首先是一個動物,而一個.NET程序集首先是一個Windows可執行程序。

那麼什麼樣格式的文件纔是一個Windows可執行文件?這個格式被稱作PE/COFF(Microsoft Windows Portable Executable/Common Object File Format),Windows可移植可執行/通用對象文件格式。Windows操做系統可以加載並運行.dll和.exe是由於它可以理解PE/COFF文件的格式。顯然,全部在Windows操做系統上運行的程序都須要符合這個格式,固然也包括.NET程序集在內。在這一級,程序的控制權還屬於操做系統,PE/COFF頭包含了供操做系統查看和利用的信息。此時,程序集能夠表示成如圖6-14所示。

圖6-14 程序集結構1

在前面提到過,程序集中包含的CIL語言代碼並非計算機能夠直接執行的,還須要進行即時編譯,那麼在對CIL語言代碼進行編譯前,須要先將編譯的環境運行起來,所以PE/COFF頭以後的就是CLR頭了。CLR頭最重要的做用之一就是告訴操做系統這個PE/COFF文件是一個.NET程序集,區別於其餘類型的可執行程序。

圖6-15 程序集結構2

在CLR頭以後就是你們相對熟悉一些的內容了。首先,程序集包含一個清單(manifest),這個清單至關於一個目錄,描述了程序集自己的信息,例如程序集標識(名稱、版本、文化)、程序集包含的資源(Resources)、組成程序集的文件等。

圖6-16 程序集結構3

清單以後就是元數據了。若是說清單描述了程序集自身的信息,那麼元數據則描述了程序集所包含的內容。這些內容包括:程序集包含的模塊(會在第7章介紹)、類型、類型的成員、類型和類型成員的可見性等。注意,元數據並不包含類型的實現,有點相似於C++中的.h頭文件。在.NET中,查看元數據的過程就叫作反射(Reflection)。

圖6-17 程序集結構4

接下來就是已經轉換爲CIL的程序代碼了,也就是元數據中類型的實現,包括方法體、字段等,相似於C++中的.cpp文件。

圖6-18 程序集結構

注意,圖6-18中還多添加了一個資源文件,例如.jpg圖片。從這幅圖能夠看出,程序集是自解釋型的(Self-Description),再也不須要任何額外的東西,例如註冊表,就能夠完整地知道程序集的一切信息。

至此對程序集的簡單介紹就先到這裏,接下來看一下程序集是如何被執行的。

6.6.2 運行程序集

如今已經瞭解過了程序集,而且知道程序集中包含的CIL代碼並不能直接運行,還須要CLR的支持。歸納來講,CLR是一個軟件層或代理,它管理了.NET程序集的執行,主要包括:管理應用程序域、加載和運行程序集、安全檢查、將CIL代碼即時編譯爲機器代碼、異常處理、對象析構和垃圾回收等。相對於編譯時(Compile time),這些過程發生在程序運行的過程當中,所以,將這個軟件層命名爲了運行時,實際上它自己與時間是沒有太大關係的。有一些朋友在初學.NET的時候,糾結在了Runtime這個詞上,總覺得和時間有什麼關係,老是不能很好地理解CLR。筆者認爲重要的是理解CLR是作什麼的,而不用過於關注它的名稱。

實際上,CLR還有一種叫法,即VES(Virtual Execution System,虛擬執行系統)。從上一段的說明來看,這個命名應該更能描述CLR的做用,也不容易引發混淆,可是可能爲了和CIL、CTS、CLS等術語保持一致性,最後將其命名爲了CLR。在這裏,咱們知道CLR不過是一個.NET程序集的運行環境而已,有點相似於Java虛擬機。VES這個術語來自於CLI,會在6.7節進行講述。

能夠用圖6-19來描述CLR的主要做用。

圖6-19 CLR的主要做用

前面已經概要地瞭解了CLR的做用,接下來開始更進一步的學習。首先遇到的問題就是:CLR以什麼樣的形式位於什麼位置?

因爲CLR自己用於管理託管代碼,所以它是由非託管代碼編寫的,並非一個包含了託管代碼的程序集,也不能使用IL DASM進行查看。它位於C:\%SystemRoot%\Microsoft.NET\Framework\版本號下,視安裝的機器不一樣有兩個版本,一個是工做站版本的mscorwks.dll,一個是服務器版本的mscorsvr.dll。wks和svr分別表明work station和server。

接下來再看一下CLR是如何運行起來的。雖然從Windows Server 2003開始,.NET框架已經預裝在操做系統中,可是它尚未集成爲操做系統的一部分。當操做系統嘗試打開一個託管程序集(.exe)時,它首先會檢查PE頭,根據PE頭來建立合適的進程。

接下來會進一步檢查是否存在CLR頭,若是存在,就會當即載入MsCorEE.dll。這個庫文件是.NET框架的核心組件之一,注意它也不是一個程序集。MsCorEE.dll位於C:\%SystemRoot%\System32\系統文件夾下全部安裝了.NET框架的計算機都會有這個文件。你們可能注意到了,這個庫安裝在System32系統文件夾下,而沒有像其餘的核心組件或類庫那樣按照版本號存放在C:\%SystemRoot%\Microsoft.NET\Framework\文件夾下。這裏又存在一個「雞生蛋問題」:根據不一樣的程序集信息會加載不一樣版本的CLR,所以加載CLR的組件就應該只有一個,不能再根據CLR的版本去決定加載CLR的組件的版本。

MsCorEE.dll是一個很細的軟件層。加載了MsCorEE.dll以後,會調用其中的_CorExeMain()函數,該函數會加載合適版本的CLR。在CLR運行以後,程序的執行權就交給了CLR。CLR會找到程序的入口點,一般是Main()方法,而後執行它。這裏又包含了如下過程:

  1. 加載類型。在執行Main()方法以前,首先要找到擁有Main()方法的類型而且加載這個類型。CLR中一個名爲Class loader(類加載程序)的組件負責這項工做。它會從GAC、配置文件、程序集元數據中尋找這個類型,而後將它的類型信息加載到內存中的數據結構中。在Class loader找到並加載完這個類型以後,它的類型信息會被緩存起來,這樣就無需再次進行相同的過程。在加載這個類之後,還會爲它的每一個方法插入一個存根(stub)。
  2. 驗證。在CLR中,還存在一個驗證程序(verifier),該驗證程序的工做是在運行時確保代碼是類型安全的。它主要校驗兩個方面,一個是元數據是正確的,一個是CIL代碼必須是類型安全的,類型的簽名必須正確。
  3. 即時編譯。這一步就是將託管的CIL代碼編譯爲能夠執行的機器代碼的過程,由CLR的即時編譯器(JIT Complier)完成。即時編譯只有在方法的第一次調用時發生。回想一下,類型加載程序會爲每一個方法插入一個存根。在調用方法時,CLR會檢查方法的存根,若是存根爲空,則執行JIT編譯過程,並將該方法被編譯後的本地機器代碼地址寫入到方法存根中。當第二次對同一方法進行調用時,會再次檢查這個存根,若是發現其保存了本地機器代碼的地址,則直接跳轉到本地機器代碼進行執行,無需再次進行JIT編譯。

能夠看出,採用這種架構的一個好處就是,.NET程序集能夠運行在任何平臺上,無論是Windows、UNIX,仍是其餘操做系統,只要這個平臺擁有針對於該操做系統的.NET框架就能夠運行.NET程序集。

6.7 CLI——公共語言基礎

CLI是一個國際標準,由ECMA和ISO進行了標準化,全稱爲Common Language Infrastructure(公共語言基礎)。它只是一個概念和彙總,實際上本章的每一小節都是這個標準的一部分。CLI包括:CIL、CTS、CLS、VES、元數據、基礎框架。

看到這裏不少人會感受到有點奇怪,爲何CLI和.NET框架包含的內容如此雷同?它們之間是什麼關係?簡單來講,CLI是一個標準,而.NET框架是這個標準的具體實現。在CLI中,並無CLR的概念,只有VES,而CLR就是.NET框架中VES的具體實現。既然CLI只是一個標準,而.NET框架是它在Windows平臺上的具體實現,那麼是否是就只有.NET框架這一個CLI的實現?顯然不是,Mono Project就是CLI標準的另外一個實現。Mono Project的目標就是將.NET框架多平臺化,使其能夠運行在各類平臺上,包括Mac OS、Linux等。

CLI的詳細信息能夠在這裏查看:http://www.ecma-international.org/publications/standards/Ecma-335.htm,感興趣的朋友能夠將它的PDF標準文檔下載下來看一下。

6.8 本章小結

本章系統的學習地介紹了一下.NET框架的底層知識,幾乎包含了常見的全部術語,例如程序集、CIL、CTS、CLS、CLR等,同時也介紹了它們之間是如何相互協做共同構建起整個.NET平臺的。相信通過本章的學習,你們會對.NET框架有一個更好的全局性認識。

感謝閱讀,但願這篇文章能給你帶來幫助。

 

出處:http://www.cnblogs.com/JimmyZhang/archive/2012/11/27/2790759.html

相關文章
相關標籤/搜索