首頁  >  文章  >  後端開發  >  C#中CLR(公共語言執行時期)與IL(中間程式碼)

C#中CLR(公共語言執行時期)與IL(中間程式碼)

黄舟
黄舟原創
2017-02-07 15:44:011863瀏覽

.net平台中的CLR

首先要說明的是,.NET平台與C#不是一回事 它是C#,VB.net等程式運作的平台。

CLR是公用語言執行時,是 .NET Framework的重要組成部分。它提供了記憶體管理、線程管理和異常處理等服務,而且還負責對程式碼實施嚴格的類型安全檢查,保證了程式碼的正確性。

事實上,類型安全(Type Checker)、垃圾回收(Garbage Collector)、異常處理(Exception Manager)、向下相容(COM Marshaler)等很多C#中的特性都是由CLR來提供的。

什麼是IL

.NET Framework是架構在Windows平台上的一個虛擬的運行平台,你可以想像將最下層Windows換做其他的作業系統,例如說Linux,一樣可以實現使用符合CLS(Common Language Specification,通用語言規範)的.NET語言,這其實就是Mono計畫要實現的功能。因而,理論上,C#是一種可以跨平台的語言。

C#另一個比較象Java的地方是,它也是一種(特殊意義上的)語言,同Java一樣,C#編寫的程式碼也是先透過C#編譯器編譯為一種特殊的位元組程式碼, ( Microsoft Intermediate Language,MSIL,微軟)中間語言,運行時再經由特定的編譯器(JIT編譯器,Just In tIME, JITer)編譯為機器碼,以供作業系統執行。

IL是一門中間語言 ,.NET平台上的各種高階語言(如C#,VB,F#)的編譯器會將各自的文字表述方式轉換為IL。各種不同的文字形式最終被統一到了IL的表述方式

CLR加載了IL之後,當每個方法第一次被執行時,就會使用JIT將IL代碼進行編譯為機器碼,機器碼和彙編其實也是一一對應的,可以這樣理解:彙編是機器碼的文字表現形式,提供了一些方便人們記憶的「助記符」。

對於同樣的IL,JIT會把它為不同的CPU架構(如x86/IA64等等)產生不同的機器碼。

C#程式碼及其對應的IL中間程式碼

C#中CLR(公共語言執行時期)與IL(中間程式碼)

//hidebysig指令表示如果当前类为父类,用该指令标记的方法将不会被子类继承
//cil managed表明方法体中的代码是IL代码,且是托管代码,即运行在CLR运行库上的代码
.method private hidebysig static void Main(string[] args)cil managed
{
    .entrypoint //该指令代表该函数程序的入口函数。每一个托管应用程序都有且只有一个入口函数,CLR加载程序时,首先从.entrypoint函数开始执行。
    .maxstack 2 //执行构造函数时,评估堆栈可容纳数据项的最大个数。评估堆栈是保存方法中所需要变量的值的一个内存区域,该区域在方法执行结束时会被清空,
    或者存储一个返回值。
    .locals init (
        [0] int32 num,
        [1] int32 num2,
        [2] int32 num3) //表示定义int类型的变量,变量名分别为num,num2,num3。存储在调用栈。
    L_0000: nop  //No operation的意思,即没有任何操作。
    L_0001: ldc.i4.1  //将“1”压入评估栈,此时“1”处于评估栈的栈顶。
    L_0002: stloc.0  //此指令表示把值从评估栈中弹出,并赋值给调用栈的第0个变量num。
    L_0003: ldc.i4.2
    L_0004: stloc.1
    L_0005: ldc.i4.3
    L_0006: stloc.2 //从.locals init到L_0006,相当于C#代码的为i,j,k赋值。
    L_0007: ldloc.0  //取调用栈中位置为0的元素压入评估栈(取i的值)。
    L_0008: ldloc.1  //取调用栈中位置为1的元素压入评估栈(取j的值)。
    L_0009: add  //做加法操作
    L_000a: ldloc.2 //取调用栈中位置为2的元素压入评估栈(取k的值)。
    L_000b: add  //做加法操作
    L_000c: call void [mscorlib]System.Console::WriteLine(int32) //调用输出方法
    L_0011: nop  //No Operation
    L_0012: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey() //调用ReadKey方法
    L_0017: pop //把评估栈的内容清空
    L_0018: ret  //return 标记返回值
} //Main方法结束

透過上面的程式碼,我們可以總結一下: .maxstack:程式碼中變數需要在呼叫堆疊(Call Stack)中佔用幾個位置; .locals int(……):定義變數初始化並放入呼叫堆疊中(Call Stack); nop:No Operation,沒有任何操作;ldstr:Load String,將字串壓入評估堆疊(Evaluation Stack);


ldc.i4.1:把數值2以4位元組長度整數的形式壓入評估棧; stloc:把評估棧(Evaluation)中的值彈出賦值到呼叫棧(Call Stack);ldloc:把呼叫堆疊(Call Stack)中指定位置的值取出(Copy)壓入評估棧(Evaluation Stack)中; call:呼叫指定的方法,這個指令一般用於呼叫靜態方法;而callvir則一般用於呼叫實例方法; ret:return ,標記返回。

下面再看一個例子

namespace TestConsole
{
    class Program
    {
        [MethodImpl(MethodImplOptions.NoInlining)]
        private static void SomeMethod()
        {
            Console.WriteLine("Hello World!");
        }
 
        static void Main(string[] args)
        {
            Console.WriteLine("Before JITed.");
            Console.ReadLine();
 
            SomeMethod();
 
            Console.WriteLine("After JITed");
            Console.ReadLine();
        }
    }
}

與之相對應的main方法IL代碼:

.method private hidebysig static void Main(string[] args) cil managed
{
    .entrypoint
    .maxstack 8
    // 分配字符串"Before JITed"
    L_0000: ldstr "Before JITed."
    // 调用Console.WriteLine方法
    L_0005: call void [mscorlib]System.Console::WriteLine(string)
    // 调用Console.ReadLine方法
    L_000a: call string [mscorlib]System.Console::ReadLine()
    L_000f: pop
    // 调用Program.SomeMethod方法
    L_0010: call void TestConsole.Program::SomeMethod()
    // 分配字符串"After JITed"
    L_0015: ldstr "After JITed"
    // 调用Console.WriteLine方法
    L_001a: call void [mscorlib]System.Console::WriteLine(string)
    // 调用Console.ReadLine方法
    L_001f: call string [mscorlib]System.Console::ReadLine()
    L_0024: pop
    L_0025: ret
}

以上就是C#中CLR(公共語言運行時)與IL(中間代碼)的內容,更多相關內容請關注PHP中文網(www.php.cn)!


陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn