【教程】【轉載】C#銳利體驗(李建忠)—01
2024-07-21 02:25:10
供稿:網友
,歡迎訪問網頁設計愛好者web開發。c#銳利體驗
南京郵電學院 李建忠([email protected])
c#語言是一門簡單,現代,優雅,面向對象,類型安全,平臺獨立的一門新型組件編程語言。其語法風格源自c/c++家族,融合了visual basic的高效和c/c++強大,是微軟為奠定其下一互聯網霸主地位而打造的microsoft.net平臺的主流語言。其一經推出便以其強大的操作能力,優雅的語法風格,創新的語言特性,第一等的面向組件編程的支持而深受世界各地程序員的好評和喜愛。“它就是我多年來夢寐以求的計算機語言!”--很多資深程序員拿到c#都是這樣的驚訝。從c#語言的名字(c sharp)我們也可見微軟用其打造其下一代互聯網絡深度服務的勃勃雄心。c#語言目前已由微軟提交歐洲計算機制造商協會ecma,經過標準化后的c#將可由任何廠商在任何平臺上實現其開發工具及其支持軟件,這為c#的發展提供了強大的驅動力,我們也可從這里看到微軟前所未有的眼光和智慧。組件編程已經成為當今世界軟件業面向下一代程序開發的一致選擇,是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
//csc helloworld.cs
using system;
class helloworld
{
public static void main()
{
console.writeline("hello world !");
}
}
我們可以打開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)。這樣的解釋可能還是有點讓人困惑,那么我們來實際的解剖一下這個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!