程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 菜鳥CLR VIA C#之旅—開始旅行:千裡之行始於足下

菜鳥CLR VIA C#之旅—開始旅行:千裡之行始於足下

編輯:C#入門知識

菜鳥踏上CLR VIA C#之旅,小學成績一般般,初中成績普普通通,高中成績勉勉強強,三流大學湊活畢業。偶然間踏上了北京之旅,也開始了他作為程序員的夢想。就這麼一個擺在大街上不會有人再看第二眼的普通到無法再普通的普通人,就是本次旅行的菜鳥了。希望《CLR VIA C#》這本聖經能帶菜鳥踏上更高的台階!。

 

順便說下菜鳥學習.net的經過。菜鳥是09年10月份(大三上學期)接觸C#的,自己買了本《C#入門經典》自己看的。然後又買了本《C#高級編程》,就這樣走上了.net之路,在剛開始自學的時候,面向對象的基礎知識就像一道門檻時時阻礙著菜鳥,在此要特別感謝菜鳥的室友,大家都叫他果爺,就菜鳥個人認為,他是我們專業技術最好的,不管菜鳥的問題多幼稚或簡單,他都會認真解答。

 

到了今年3月份時,菜鳥進入了北京一家金融軟件開發公司實習,一直到現在。在工作中發現了自己有很大的弊病,由於自己剛剛畢業,覺得很多事情自己不懂是理所當然。我發現自己錯了,錯的離譜,所以請記住,學習不是被動的,當任何事務變成被動的時候,那件事已經不再是你的事了!

 

菜鳥從小就是一個有理想的人,但目前菜鳥必須先在北京把自己養活,然後才能發展,隨著工作越深,菜鳥心中的擔心、空虛和迷茫就越多!但菜鳥相信,任何東西,只要一個人認真地去學,投入很多時間和精力,肯定是能夠干好的。在學C#的過程中真的體會到了“學的越多,才知道會的越少”這句話的含義。學了語法之後會發現很多Framework中的類不太會用,學了一些類庫的用法又發現還有MSIL、CLR和JIT、GC這些高深的東西。這才發現.NET的寬廣和駁雜,對同學們的“其實.NET就這點兒東西,拖拖控件而已”的說法頗不贊同。就在意識到.NET的博大精深的同時,菜鳥發現自己一直受困在目前的水平中,想要自我提高,很難很難。知道要學的東西有一大堆,但菜鳥卻是根本不知道到底要學些什麼東西知識才能讓我得到快速進步,!這才是讓人最痛苦的事!菜鳥也經常查閱這方面的信息,但似乎總是很亂,從沒有一個人能夠詳細透徹地給菜鳥以明確的指引。

 

在偶然的一個晚上,菜鳥躺在床上的時候,突然有件事物觸動了菜鳥的靈感,對啊,怎麼把它給忘了,它就是一直默默守候在菜鳥的床頭桌上的《CLR VIA C#》,記得周公說過:“如果你沒有時間的話,那麼學新技術不如學基礎、學應用不如學思想。技術雖然一直在變,但是越是基礎和越是抽象的技術變化越慢,越是偏向應用越是具體的技術變化越是快,從性價比上說,學習基礎知識性價比更高。”說實話,這本書菜鳥買了有段時間了,一直沒有靜心下來認真學習。菜鳥准備系統學習這本偉大的書,並將自己的心得體會寫下來,權當學習筆記,加深印象及理解,技術的深度來自於總結。菜鳥知識有限,文章中難免會有很多低級可笑的錯誤,希望得到各位高人的指正批評,這樣菜鳥進步的會更快!

 

好了,上面廢話了這麼多,菜鳥給大家演示一個最簡單的例子—Hello,word。1988年Brian W. Kernighan和Dennis M. Ritchie合著了軟件史上的經典巨著《The C programming Language》,從那時起,Hello, world示例就作為了幾乎所有實踐型程序設計書籍的開篇代碼,一直延續至今,除了表達對巨人與歷史的尊重,本文也以Hello, world示例作為我們扣開學習的大門,開始菜鳥循序漸進的CLR VIA C#認識之旅。

從Hello word開始:


1 using System;2 3     class HelloWord4     {5         static void Main(string[] args)6         {7             Console.WriteLine("Hello,word!");8         }9     }    相信即使是剛接觸的人對這段代碼都不陌生,是的,它向陌生的世界打了個招呼,那麼運行在高級語言的真相又是什麼呢,讓菜鳥來為大家進一步剖析,對編譯後的可執行文件Hello word.exe應用VS自帶的ILDasm.exe反編譯工具打開,在這裡,菜鳥給大家介紹一個容易被忽視的VS技巧:

配置外部工具


對於開發中我們可能會經常用到一些外部工具,比如ILDASM和ILASM等,我們可以配置成Visual Studio 2010的一個菜單項,需要的時候點擊一下就可以打開,而不必每次都費時費力找到該文件所在的位置再雙擊打開。配置過程如下:
    從工具欄上找到“工具”-“外部工具”,如下圖:

\
  

 

   將ildasm.exe路徑設置下(電腦中ildasm.exe文件位置),比如菜鳥電腦:

 C:\Program Files\Microsoft SDKs\Windows\v7.0A\bin\ildasm.exe,標題隨便自己設置,如下:

 \

 

 

配置好以後就可以直接在工具欄中運行了:

\
     

 

 好了,說遠了,言歸正傳,還是來看我們的 Hello word的MSIL編碼,用VS自帶的ILDasm.exe反編譯工具打開後如下:

 \

 

    由上圖可知,編譯後的IL結構中,包含了MANIFEST和HelloWorld類,其中MANIFEST是個附加信息列表,主要包含了程序集的一些屬性,例如程序集名稱、版本號、哈希算法、程序集模塊等,以及對外部引用程序集的引用項;而Hello_world類則是我們下面介紹的主角。


    首先我們來看一下MANIFEST程序集清單分析:

 1 // Metadata version: v4.0.30319 2 .assembly extern mscorlib 3 { 4   .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4.. 5   .ver 4:0:0:0 6 } 7 .assembly 'Hello word' 8 { 9   .custom instance void [mscorlib]System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(string) = ( 01 00 29 2E 4E 45 54 46 72 61 6D 65 77 6F 72 6B   // ..).NETFramework10                                                                                                         2C 56 65 72 73 69 6F 6E 3D 76 34 2E 30 2C 50 72   // ,Version=v4.0,Pr11                                                                                                         6F 66 69 6C 65 3D 43 6C 69 65 6E 74 01 00 54 0E   // ofile=Client..T.12                                                                                                         14 46 72 61 6D 65 77 6F 72 6B 44 69 73 70 6C 61   // .FrameworkDispla13                                                                                                         79 4E 61 6D 65 1F 2E 4E 45 54 20 46 72 61 6D 65   // yName..NET Frame14                                                                                                         77 6F 72 6B 20 34 20 43 6C 69 65 6E 74 20 50 72   // work 4 Client Pr15                                                                                                         6F 66 69 6C 65 )                                  // ofile16   .custom instance void [mscorlib]System.Reflection.AssemblyTitleAttribute::.ctor(string) = ( 01 00 0A 48 65 6C 6C 6F 20 77 6F 72 64 00 00 )    // ...Hello word..17   .custom instance void [mscorlib]System.Reflection.AssemblyDescriptionAttribute::.ctor(string) = ( 01 00 00 00 00 ) 18   .custom instance void [mscorlib]System.Reflection.AssemblyConfigurationAttribute::.ctor(string) = ( 01 00 00 00 00 ) 19   .custom instance void [mscorlib]System.Reflection.AssemblyCompanyAttribute::.ctor(string) = ( 01 00 0C E5 BE AE E8 BD AF E4 B8 AD E5 9B BD 00 20                                                                                                 00 ) 21   .custom instance void [mscorlib]System.Reflection.AssemblyProductAttribute::.ctor(string) = ( 01 00 0A 48 65 6C 6C 6F 20 77 6F 72 64 00 00 )    // ...Hello word..22   .custom instance void [mscorlib]System.Reflection.AssemblyCopyrightAttribute::.ctor(string) = ( 01 00 1E 43 6F 70 79 72 69 67 68 74 20 C2 A9 20   // ...Copyright .. 23                                                                                                   E5 BE AE E8 BD AF E4 B8 AD E5 9B BD 20 32 30 31   // ............ 20124                                                                                                   31 00 00 )                                        // 1..25   .custom instance void [mscorlib]System.Reflection.AssemblyTrademarkAttribute::.ctor(string) = ( 01 00 00 00 00 ) 26   .custom instance void [mscorlib]System.Runtime.InteropServices.ComVisibleAttribute::.ctor(bool) = ( 01 00 00 00 00 ) 27   .custom instance void [mscorlib]System.Runtime.InteropServices.GuidAttribute::.ctor(string) = ( 01 00 24 38 64 66 39 39 65 34 64 2D 66 38 64 66   // ..$8df99e4d-f8df28                                                                                                   2D 34 62 62 34 2D 61 61 63 34 2D 35 35 31 38 34   // -4bb4-aac4-5518429                                                                                                   39 63 31 65 62 64 62 00 00 )                      // 9c1ebdb..30   .custom instance void [mscorlib]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( 01 00 07 31 2E 30 2E 30 2E 30 00 00 )             // ...1.0.0.0..31 32   // --- 下列自定義屬性會自動添加,不要取消注釋 -------33   //  .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) 34 35   .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) 36   .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78   // ....T..WrapNonEx37                                                                                                              63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 )       // ceptionThrows.38   .hash algorithm 0x0000800439   .ver 1:0:0:040 }41 .module 'Hello word.exe'42 // MVID: {6CF52DF9-4A2D-4815-91B4-D85F23F6E333}43 .imagebase 0x0040000044 .file alignment 0x0000020045 .stackreserve 0x0010000046 .subsystem 0x0003       // WINDOWS_CUI47 .corflags 0x00000003    //  ILONLY 32BITREQUIRED48 // Image base: 0x04C90000
從這段清單代碼中,我們可以得到如下信息:

.assembly指令用於定義編譯目標或者加載外部庫。在IL清單中可見,.assembly extern mscorlib表示外部加載了外部核心庫mscorlib,而.assembly HelloWorld則表示了定義的編譯目標。值得注意的是,.assembly將只顯示程序中實際應用到的程序集列表,而對於加入using引用的程序集,如果並未在程序中引用,則編譯器會忽略多加載的程序集,例如System.Data將被忽略,這樣就有效避免了過度加載引起的代碼膨脹。
我們知道mscorlib.dll程序集定義managed code依賴的核心數據類型,屬於必須加載項。 例如接下來要分析的.ctor指令表示構造函數,從代碼中我們知道沒有為HelloWord類提供任何顯示的構造函數,因此可以肯定其繼承自基類System.Object,而這個System.Object就包含在mscorlib程序集中。
在外部指令中還會指明了引用版本(.ver);應用程序實際公鑰標記(.publickeytoken),公鑰Token是SHA1哈希碼的低8位字節的反序(如下圖所示),用於唯一的確定程序集;還包括其他信息如語言文化等。

HelloWorld程序集中包括了.hash algorithm指令,表示實現安全性所使用的哈希算法,系統缺省為0x00008004,表明為SHA1算法;.ver則表示了HelloWorld程序集的版本號;
程序集由模塊組成, .module為程序集指令,表明定義的模塊的元數據,以指定當前模塊。
其他的指令還有:imagebase為影像基地址;.file alignment為文件對齊數值;.subsystem為連接系統類型,0x0003表示從控制台運行;.corflags為設置運行庫頭文件標志,默認為1;這些指令不是我們研究的重點,詳細的信息請參考MSDN相關信息。       指令
 說明
 
.assembly extern <程序集名稱>
 指定包含當前模塊所引用項目的另一程序集(在此示例中為 mscorlib)。
 
.publickeytoken <標記>
 指定所引用程序集的實際密鑰的標記。
 
.ver <版本號>
 指定引用程序集的版本號。
 
.assembly <程序集名稱>
 指定程序集名稱。
 
.hash algorithm <int32 值>
 指定使用的哈希算法。
 
.ver <版本號>
 指定程序集的版本號。
 
.module <文件名>
 指定組成程序集的模塊名稱, 在此示例中,程序集只包含一個文件。
 
.subsystem <值>
 指定程序要求的應用程序環境。 在此示例中,值 3 表示該可執行文件從控制台運行。
 
.corflags
 當前是元數據中的一個保留字段。
 

    下面是重點的Hello_word類分析:

    首先我們看看定義HelloWord類是IL是如何處理的

 

1 .class private auto ansi beforefieldinit HelloWord2        extends [mscorlib]System.Object3 {4 } // end of class HelloWord  Ø  .class表明了HelloWorld是一個private類(默認私有),該類繼承自外部程序集mscorlib的System.Object類。

  Ø  private為訪問控制權限,這點很容易理解。

  Ø  auto表明程序加載時內存的布局是由CLR決定的,而不是程序本身

  Ø  ansi屬性則為了在沒有被管理和被管理代碼間實現無縫轉換。沒有被管理的代碼,指的是沒有運行在CLR運行庫之上的代碼,例如原來的C,C++代碼等。

  Ø beforefieldinit屬性為HelloWorld提供了一個附加信息,用於標記運行庫可以在任何時候執行類型構造函數方法,只要該方法在第一次訪問其靜態字段之前執行即可。如果沒有beforefieldinit則運行庫必須在某個精確時間執行類型構造函數方法,從而影響性能優化。

 

   然後是.ctor方法,代碼為:

1 .method public hidebysig specialname rtspecialname 2         instance void  .ctor() cil managed3 {4   // 代碼大小       7 (0x7)5   .maxstack  86   IL_0000:  ldarg.07   IL_0001:  call       instance void [mscorlib]System.Object::.ctor()8   IL_0006:  ret9 } // end of method HelloWord::.ctor
Ø  cil managed 說明方法體中為IL代碼,指示編譯器編譯為托管代碼。

Ø  .maxstack表明執行構造函數.ctor期間的評估堆棧(Evaluation Stack)可容納數據項的最大個數。關於評估堆棧,其用於保存方法所需變量的值,並在方法執行結束時清空,或者存儲一個返回值。

Ø  IL_0000,是一個標記代碼行開頭,一般來說,IL_之前的部分為變量的聲明和初始化。

Ø  ldarg.0 表示裝載第一個成員參數,在實例方法中指的是當前實例的引用,該引用將用於在基類構造函數中調用。

Ø  call指令一般用於調用靜態方法,因為靜態方法是在編譯期指定的,而在此調用的是構造函數.ctor()也是在編譯期指定的;而另一個指令callvirt則表示調用實例方法,它的調用過程有異於call,函數的調用是在運行時確定的,首先會檢查被調用函數是否為虛函數,如果不是就直接調用,如果是則向下檢查子類是否有重寫,如果有就調用重寫實現,如果沒有還調用原來的函數,依次類推直到找到最新的重寫實現。

Ø  ret表示執行完畢,返回。

 

最後是Main方法,代碼為:

 1 .method private hidebysig static void  Main(string[] args) cil managed 2 { 3   .entrypoint 4   // 代碼大小       13 (0xd) 5   .maxstack  8 6   IL_0000:  nop 7   IL_0001:  ldstr      "Hello,word!" 8   IL_0006:  call       void [mscorlib]System.Console::WriteLine(string) 9   IL_000b:  nop10   IL_000c:  ret11 } // end of method HelloWord::Main
Ø   hidebysig屬性用於表示如果當前類作為父類時,類中的方法不會被子類繼承,因此HelloWorld子類中不會看到Main方法。


Ø  .entrypoint指令表明了CLR加載程序HelloWorld.exe時,是首先從.entrypoint方法開始執行的,也就是表明Main方法將作為程序的入口函數。每個托管程序必須有並且只有一個入口點。這區別於將Main函數作為程序入口標志。


Ø  ldstr指令表示將字符串壓棧,"Hello, world."字符串將被移到stack頂部。CLR通過從元數據表中獲得文字常量來構造string對象,值得注意的是,在此構造string對象並未出現newobj指令。


關於字符串string的卻比較特殊,String類型直接派生自Object,所以它是引用類型,總是存在於堆上,永遠不會跑到線程棧,但必須注意的是不能使用new操作符來從一個文本常量字符串來構造一個String對象


 1 using System; 2  3     class HelloWord 4     { 5         static void Main(string[] args) 6         { 7             //string a="abc"; 8             string a = new string("abc");  //錯誤 9         }10     }

     相反,必須使用代碼中注釋的簡化語法來構造,為什麼?我們知道IL指令newobj用於構造一個對象的新實例,然而,在實例化String對象時IL代碼中並不會出現newobj指令,只有一個ldstr(即load string)指令,它用從元數據獲得的一個文本常量字符串構造一個String對象,這證明CLR事實使用一種特殊的方式來構造文本常量String對象,或許這就是String與眾不同(不變性、字符串駐留)的根源所在吧!

   好了,這篇不是文章的文章就寫到這了,菜鳥的CLR VIA C#之旅開始了,菜鳥已經准備好了,你呢?

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved