組件編程已經成為當今世界軟件業面向下一代程序開發的一致選擇,是90年代面向對象編程的深度發展。C#生逢其時,占盡天時地利,“第一等的面向組件編程的支持”也決不是簡單說說那麼輕松。實際上,組件特性已經深深植入C#語言的各個層面,是為C#銳利(Sharp)之處。在下面的文章中筆者將從C#語言的各個層面來展現C#語言中無處不見的組件特性,深度闡述C#面向組件編程。整個專題共分為十講:“第一講 ‘Hello,World!'程序”,“第二講 C#語言基礎介紹”,“第三講 Microsoft.NET平台基礎構造”,“第四講 類與對象”,“第五講 構造器與析構器”,“第六講 方法”,“第七講 域與屬性”,“第八講 索引器與操作符重載”,“第九講 數組與字符串”,“第十講 特征與映射”,“第十一講 COM互操作 非托管編程與異常處理”,“第十二講 用C#編織未來--C#編程模型概述”。
第一講 “Hello,World!”程序
“Hello World!”程序是程序員一直以來的一個浪漫約定,也是一個偉大的夢想--總有一天,出自人類之手的計算機會面對這個美麗的世界說一聲“Hello World!”。它是學習一門新語言的一個很好的起點,我們就從這裡開始,看下面例子:
//HelloWorld.cs by Cornfield,2001我們可以打開Windows自帶的簡易的"記事本"程序來編寫這段代碼--筆者推薦剛開始采用這個極其簡單卻能把程序代碼暴露的相當清晰的編輯工具。我們將它的文件名保存為HelloWorld.cs,其中".cs"是C#源代碼文件的擴展名。然後在配置好C#編譯器的命令行環境裡鍵入"csc HelloWorld.cs"編譯文件。可以看到編譯輸出文件HelloWorld.exe。我們鍵入HelloWorld執行這個文件可得到下面的輸出:
Hello World !
下面我們來仔細分析上面的代碼和整個程序的編譯輸出及執行過程。先看文件開始的兩行代碼,這是C#語言的單行注釋語句。和C++語言類似,C#支持兩種注釋方法:以"//"開始的單行注釋和以"/*","*/"配對使用的多行注釋。注釋之間不能嵌套。
再來看下面的"using System;"語句,這是C#語言的using命名空間指示符,這裡的"System"是Microsoft.NET系統提供的類庫。C#語言沒有自己的語言類庫,它直接獲取Microsoft.NET系統類庫。Microsoft.NET類庫為我們的編程提供了非常強大的通用功能。該語句使得我們可以用簡短的別名"Console"來代替類型"System.Console"。當然using指示符並不是必須的,我們可以用類型的全局名字來獲取類型。實際上,using語句采用與否根本不會對C#編譯輸出的程序有任何影響,它僅僅是簡化了較長的命名空間的類型引用方式。
接著我們聲明並實現了一個含有靜態Main()函數的HelloWorld類。C#所有的聲明和實現都要放在同一個文件裡,不像C++那樣可以將兩者分離。Main()函數在C#裡非常特殊,它是編譯器規定的所有可執行程序的入口點。由於其特殊性,對Main()函數我們有以下幾條准則:
Main()函數必須封裝在類或結構裡來提供可執行程序的入口點。C#采用了完全的面向對象的編程方式,C#中不可以有像C++那樣的全局函數。
Main()函數必須為靜態函數(static)。這允許C#不必創建實例對象即可運行程序。
Main()函數保護級別沒有特殊要求, public,protected,private等都可,但一般我們都指定其為public。
Main()函數名的第一個字母要大寫,否則將不具有入口點的語義。C#是大小寫敏感的語言。
Main()函數的參數只有兩種參數形式:無參數和string 數組表示的命令行參數,即static void Main()或static void Main(string[]args) ,後者接受命令行參數。一個C#程序中只能有一個Main()函數入口點。其他形式的參數不具有入口點語義,C#不推薦通過其他參數形式重載Main()函數,這會引起編譯警告。
Main()函數返回值只能為void(無類型)或int(整數類型)。其他形式的返回值不具有入口點語義。
我們再來看"HelloWorld.cs"程序中Main()函數的內部實現。前面提過,Console是在命名空間System下的一個類,它表示我們通常打交道的控制台。而我們這裡是調用其靜態方法WriteLine()。如同C++一樣,靜態方法允許我們直接作用於類而非實例對象。WriteLine()函數接受字符串類型的參數"Hello World !",並把它送入控制台顯示。如前所述,C#沒有自己的語言類庫,它直接獲取Microsoft.NET系統類庫。我們這裡正是通過獲取Microsoft.NET系統類庫中的System.Console.WriteLine()來完成我們想要的控制台輸出操作。這樣我們便完成了"Hello World!"程序。
但事情遠沒那麼簡單!在我們編譯輸出執行程序的同時,Microsoft.NET底層的諸多機制卻在暗地裡湧動,要想體驗C#的銳利,我們沒有理由忽視其背靠的Microsoft.NET平台。實際上如果沒有Microsoft.NET平台,我們很難再說C#有何銳利之處。我們先來看我們對"HelloWorld.cs"文件用csc.exe命令編譯後發生了什麼。是的,我們得到了HelloWorld.exe文件。但那僅僅是事情的表象,實際上那個HelloWorld.exe根本不是一個可執行文件!那它是什麼?又為什麼能夠執行?
好的,下面正是回答這些問題的地方。首先,編譯輸出的HelloWorld.exe是一個由中間語言(IL),元數據(Metadata)和一個額外的被編譯器添加的目標平台的標准可執行文件頭(比如Win32平台就是加了一個標准Win32可執行文件頭)組成的PE(portable executable,可移植執行體)文件,而不是傳統的二進制可執行文件--雖然他們有著相同的擴展名。中間語言是一組獨立於CPU的指令集,它可以被即時編譯器Jitter翻譯成目標平台的本地代碼。中間語言代碼使得所有Microsoft.NET平台的高級語言C#,VB.NET,VC.NET等得以平台獨立,以及語言之間實現互操作。元數據是一個內嵌於PE文件的表的集合。元數據描述了代碼中的數據類型等一些通用語言運行時(Common Language Runtime)需要在代碼執行時知道的信息。元數據使得.NET應用程序代碼具備自描述特性,提供了類型安全保障,這在以前需要額外的類型庫或接口定義語言(Interface Definition Language,簡稱IDL)。
這樣的解釋可能還是有點讓人困惑,那麼我們來實
[1] [2] 下一頁
際的解剖一下這個PE文件。我們采用的工具是.NET SDK Beta2自帶的ildasm.exe,它可以幫助我們提取PE文件中的有關數據。我們鍵入命令"ildasm /output:HelloWorld.il HelloWorld.exe",一般可以得到兩個輸出文件:helloworld.il和helloworld.res。其中後者是提取的資源文件,我們暫且不管,我們來看helloworld.il文件。我們用"記事本"程序打開可以看到元數據和中間語言(IL)代碼,由於篇幅關系,我們只將其中的中間語言代碼提取出來列於下面,有關元數據的表項我們暫且不談:
class private auto ansi beforefieldinit HelloWorld
extends [mscorlib]System.Object
{
.method public hidebysig static void Main() cil managed
{
.entrypoint
// Code size 11 (0xb)
.maxstack 8
IL_0000: ldstr "Hello World !"
IL_0005: call void [mscorlib]System.Console::WriteLine(string)
IL_000a: ret
} // end of method HelloWorld::Main
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method HelloWorld::.ctor
} // end of class HelloWorld
我們粗略的感受是它很類似於早先的匯編語言,但它具有了對象定義和操作的功能。我們可以看到它定義並實現了一個繼承自System.Object 的HelloWorld類及兩個函數:Main()和.ctor()。其中.ctor()是HelloWorld類的構造函數,可在"HelloWorld.cs"源代碼中我們並沒有定義構造函數呀--是的,我們沒有定義構造函數,但C#的編譯器為我們添加了它。你還可以看到C#編譯器也強制HelloWorld類繼承System.Object類,雖然這個我們也沒有指定。關於這些高級話題我們將在以後的講座中予以剖析。
那麼PE文件是怎麼執行的呢?下面是一個典型的C#/.NET應用程序的執行過程:
用戶執行編譯器輸出的應用程序(PE文件),操作系統載入PE文件,以及其他的DLL(.NET動態連接庫)。
操作系統裝載器根據前面PE文件中的可執行文件頭跳轉到程序的入口點。顯然,操作系統並不能執行中間語言,該入口點也被設計為跳轉到mscoree.dll(.NET平台的核心支持DLL)的_ CorExeMain()函數入口。
CorExeMain()函數開始執行PE文件中的中間語言代碼。這裡的執行的意思是通用語言運行時按照調用的對象方法為單位,用即時編譯器將中間語言編譯成本地機二進制代碼,執行並根據需要存於機器緩存。
程序的執行過程中,垃圾收集器負責內存的分配,釋放等管理功能。
程序執行完畢,操作系統卸載應用程序。
清楚的知曉編譯輸出的PE文件的執行過程是深度掌握C#語言編程的關鍵,這種過程的本身就诠釋著C#語言的高級內核機制以及其背後Microsoft.NET平台種種詭秘的性質。一個"Hello World !"程序的概括力已經足夠,在我們對C#語言有了一個很好的起點之後,下面的專題會和大家一起領略C#基礎語言,窺探Microsoft.NET平台構造,步步體驗C#銳利編程的極樂世界,Let's go!
上一頁 [1] [2]