程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> 《C# to IL》第三章 選擇和循環

《C# to IL》第三章 選擇和循環

編輯:關於C#

-3-

在IL中,標號(label)是一個末尾帶有冒號(即:)的名稱。它使我們能夠從代碼的一部 分無條件地跳轉到另一部分。我們經常在由反編譯器生成的IL代碼中看到這個標號。例如:

IL_0000: ldstr      "hi"
IL_0005: call       void [mscorlib]System.Console::WriteLine(class System.String)
IL_000a: call       void zzz::abc()
IL_000f: ret

在冒號前面的詞就是標號。在下面給出的程序中,我們在函數abc中創建一 個名為a2的標號。指令br用於隨時跳轉到程序中的任何標號。

a.il

.assembly mukhi {}
.class private auto ansi zzz extends System.Object
{
.method public hidebysig static void vijay() il managed
{
.entrypoint
.locals (int32 V_0,class zzz V_1)
newobj instance void zzz::.ctor()
stloc.1
call int32 zzz::abc()
stloc.0
ldloc.0
call void [mscorlib]System.Console::WriteLine(int32)
ret
}
.method private hidebysig static int32 abc() il managed
{
.locals (int32 V_0)
ldc.i4.s   20
br.s a2
ldc.i4.s   30
a2: ret
}
}

Output

20

函數abc示范了這個概念。在這個函數中,代碼繞過了指令 ldc.i4.s 30。因此,返回值顯示為20而不是30。從而,IL使用br指令來無條件地跳躍到代碼的任何部分 。(程序集指令br獲取4字節,而在.sr之前的br,即br.s獲取1字節,對於每個標記為.s的指令,解釋都 是相同的。)

br指令是IL得以運轉的關鍵組件之一。

a.cs

class zzz
{
static bool i = true;
public static void Main()
{
if (i)
System.Console.WriteLine("hi");
}
}

a.il

.assembly mukhi {}
.class private auto ansi zzz extends System.Object
{
.field private static bool i
.method public hidebysig static void vijay() il managed
{
.entrypoint
ldsfld     bool zzz::i
brfalse.s IL_0011
ldstr      "hi"
call       void [mscorlib]System.Console::WriteLine(class System.String)
IL_0011: ret
}
.method public hidebysig specialname rtspecialname static void .cctor() il managed
{
ldc.i4.1
stsfld     bool zzz::i
ret
}
}

Output

hi

在我們的C#程序中,我們將靜態變量初始化為true值。

l 靜態變量,如果它們是字段,就會在靜態構造函數.cctor中被初始化。這會在上面的程序中顯 示。

l 另一方面,局部變量在它們所在的函數中被初始化。

這裡,讓人吃驚的是,使用 ldc指令將值1放置在棧上的靜態構造函數中。即使同時在C#和IL中定義了字段i,還是沒有true或false這 樣的符號。

接下來,使用stsfld將靜態變量i初始化為值1,盡管變量是布爾類型的。這就證實了 IL支持bool數據類型,它不會識別出單詞true或false。因此,在IL中,布爾值分別只是數字1或0的別名 。

布爾運算符TRUE或FALSE是由C#引進的關鍵字,用來使程序員的工作更加輕松。由於IL不直接支 持這些關鍵字,所以它會替代地使用數字1或0。

指令ldsfld把靜態變量的值加載到棧上。指令 brfalse對棧進行掃描。如果它找到了數字1,它就會將其解釋為TRUE,而如果它找到了數字0,它就會將 其解釋為FALSE。

在這個例子中,它在棧上找到的值是1或TRUE,所以它不會跳轉到標號IL_0011。 在從C#到IL的轉換中,ildasm使用以IL_開始的名稱來代替標號。

指令brfalse表示“如果 FALSE就跳轉到標號”。這不同於br,後者總是會導致一個跳轉。從而,brfalse是一個有條件的跳 轉指令。

在IL中沒有提供if語句功能的指令。C#中的if語句會被轉換為IL中的轉移(branch)指 令。我們所處的任何匯編器,都沒有像if結構體這樣的高級概念。

可以看到,我們剛剛學到的那 些知識,對於我們掌握IL是非常重要的。這將幫助我們獲得——區別關於哪個概念是IL的一部 分而哪些是由編程語言的設計者引進——的能力

尤其需要注意的是,如果IL不支持某 個特性,它就不能用任何.NET編程語言實現。從而,熟悉IL所支持的各種概念的重要性——怎 麼強調都不過分。

a.cs

class zzz
{
static bool i = true;
public static void Main()
{
if (i)
System.Console.WriteLine("hi");
else
System.Console.WriteLine("false");
}
}

a.il

.assembly mukhi {}
.class private auto ansi zzz extends System.Object
{
.field private static bool i
.method public hidebysig static void vijay() il managed
{
.entrypoint
ldsfld bool zzz::i
brfalse.s IL_0013
ldstr "hi"
call void [mscorlib]System.Console::WriteLine(class System.String)
br.s IL_001d
IL_0013: ldstr      "false"
call void [mscorlib]System.Console::WriteLine(class System.String)
IL_001d: ret
}
.method public hidebysig specialname rtspecialname static void .cctor() il managed
{
ldc.i4.1
stsfld     bool zzz::i
ret
}
}

Output

hi

在編程語言中,if-else語句是極其容易理解的,但是在IL中它 卻是相當令人困惑的。IL檢查棧上的值是1還是0。

l 如果棧上的值是1,正如這個例子中的那樣, 它調用帶有參數hi的WriteLine函數,並隨後使用無條件跳轉指令br,跳轉到標號IL_001d。

l 如 果棧上的值是0,代碼跳轉到IL_0013,並且WriteLine函數會打印出false。

從而,為了在IL中實 現if-else結構,需要一個有條件跳轉和一個無條件跳轉。如果我們使用多個if-else語句,那麼IL代碼的 復雜度就會動態增加。

現在,可以看出編譯器的編寫者的智商了。

a.cs

class zzz
{
public static void Main()
{
}
void abc( bool a)
{
if (a)
{
int i = 0;
}
if ( a)
{
int i = 3;
}
}
}

a.il

.assembly mukhi {}
.class public auto ansi zzz extends [mscorlib]System.Object
{
.field private int32 x
.method public hidebysig static void vijay() il managed
{
.entrypoint
ret
}
.method private hidebysig instance void abc(bool a) il managed
{
.locals (int32 V_0,int32 V_1)
ldarg.1
brfalse.s IL_0005
ldc.i4.0
stloc.0
IL_0005: ldarg.1
brfalse.s IL_000a
ldc.i4.3
stloc.1
IL_000a: ret
}
}

C#編程語言就更復雜了。在內部的一組括號中,我們不能創建之前已經在外部創建的變 量。上面的C#程序在語法上是正確的,因為括號都是在同一級別上。

在IL中,會稍微簡單一些。 這兩個i會變成兩個單獨的變量V_0和V_1。因此,IL不會暴露施加在變量上的任何約束。

a.cs

class zzz
{
static bool i = true;
public static void Main()
{
while (i)
{
System.Console.WriteLine("hi");
}
}
}

a.il

.assembly mukhi {}
.class private auto ansi zzz extends System.Object
{
.field private static bool i
.method public hidebysig static void vijay() il managed
{
.entrypoint
br.s IL_000c
IL_0002: ldstr      "hi"
call void [mscorlib]System.Console::WriteLine(class System.String)
IL_000c: ldsfld     bool zzz::i
brtrue.s IL_0002
ret
}
.method public hidebysig specialname rtspecialname static void .cctor() il managed
{
ldc.i4.1
stsfld     bool zzz::i
ret
}
}

當看到反匯編的代碼時,你將理解為什麼程序員不以編寫IL代碼來謀生。即使一個簡單 的while循環,在轉換為IL後都會變得驚人的復雜。

對於一個while結構,會創建一個到標號 IL_000c的無條件跳轉,它位於函數的結尾。這裡,它加載靜態變量i的值到棧上。

下一個指令 brtrue,做的事情和指令brfalse所做的正好相反。實現如下:

l 如果棧頂的值——例 如,字段i的值——是1,那麼它會跳轉到標號IL_0002。然後值hi被放到棧上並且WriteLine函 數會被調用。

l 如果棧頂的值是0,那麼程序將跳轉到ret指令。

上面的程序,正如你所看 到的那樣,並不打算停止。它會繼續流動,就像一個起源於一個巨大冰川的水流。

a.cs

class zzz
{
static int i = 2;
public static void Main()
{
i = i + 3;
System.Console.WriteLine(i);
}
}

a.il

.assembly mukhi {}
.class private auto ansi zzz extends System.Object
{
.field private static int32 i
.method public hidebysig static void vijay() il managed
{
.entrypoint
ldsfld int32 zzz::i
ldc.i4.3
add
stsfld int32 zzz::i
ldsfld int32 zzz::i
call void [mscorlib]System.Console::WriteLine(int32)
ret
}
.method public hidebysig specialname rtspecialname static void .cctor() il managed
{
ldc.i4.2
stsfld     bool zzz::i
ret
}
}

Output

5

IL沒有操作符用來做兩個數字的加法,而是使用add指令。

add指令需要用來做加法的兩個數字,也就是棧上開始的2個有效的元素。因此,ldsfld指令把靜 態變量i的值和常量值3放到棧上。隨後,add指令把它們相加並把結果放到棧上。它還會從棧上移除用來 做加法的2個數字。

一旦指令被執行了,IL中的大多數指令就會擺脫棧上的參數,也就是該指令要 操作的參數。

使用指令stsfld將靜態變量i初始化為加法的結果總和。剩下的代碼直接顯示了變量 i的值。

在IL中沒有++操作符的等價物。它會被轉換為指令ldc.i4.1。同樣,兩個數字相乘,需要 使用mul指令;相減,就使用sub指令,等等。它們在IL中都有等價物。之後的代碼保持不變。

a.cs

class zzz
{
static bool i;
static int j = 19;
public static void Main()
{
i = j > 16;
System.Console.WriteLine(i);
}
}

a.il

.assembly mukhi {}
.class private auto ansi zzz extends System.Object
{
.field private static bool i
.field private static int32 j
.method public hidebysig static void vijay() il managed
{
.entrypoint
ldsfld     int32 zzz::j
ldc.i4.s   16
cgt
stsfld     bool zzz::i
ldsfld     bool zzz::i
call void [mscorlib]System.Console::WriteLine(bool)
ret
}
.method public hidebysig specialname rtspecialname static void .cctor() il managed
{
ldc.i4.s   19
stsfld int32 zzz::j
ret
}
}

Output

True

現在我們將研究IL如何處理條件操作符。讓我們考慮C#中的 語句j>i16。IL首先把值j放到棧上,也就是常量值16的前面。隨後它會調用cgt操作,這是它首次在我 們的源代碼中出現。這個指令檢查棧上的第1個值是否大於第2個。如果是,那麼它會把值1(TRUE)放到 棧上,否則它會把值0(FALSE)放到棧上。這個值隨後被存儲到變量i中。使用WritleLine函數,就會生 成布爾值的輸出,從而我們看到顯示True。

同樣,操作符<被轉換為clt指令,它會檢查棧上的 第1個值是否小於第2個。從而我們看到IL具有它自己的一套邏輯操作符,對基本的邏輯運算進行內部處理 。

a.cs

class zzz
{
static bool i;
static int j = 19;
public static void Main()
{
i = j == 16;
System.Console.WriteLine(i);
}
}

a.il

.assembly mukhi {}
.class private auto ansi zzz extends System.Object
{
.field private static bool i
.field private static int32 j
.method public hidebysig static void vijay() il managed
{
.entrypoint
ldsfld     int32 zzz::j
ldc.i4.s   16
ceq
stsfld     bool zzz::i
ldsfld     bool zzz::i
call void [mscorlib]System.Console::WriteLine(bool)
ret
}
.method public hidebysig specialname rtspecialname static void .cctor() il managed
{
ldc.i4.s   19
stsfld int32 zzz::j
ret
}
}

Output

False

操作符==就是EQUALITY操作符。它也需要棧上的2個操作數 (operand)來檢查相等性。此後它使用ceq指令來檢查相等性。如果相等,它會把值1(TRUE)放到棧上 ,如果不相等,它會把值0(FALSE)放到棧上。指令ceq是IL的邏輯指令集的不可缺少的一部分。

a.cs

class zzz
{
static bool i;
static int j = 19;
public static void Main()
{
i = j >= 16;
System.Console.WriteLine(i);
}
}

a.il

.assembly mukhi {}
.class private auto ansi zzz extends System.Object
{
.field private static bool i
.field private static int32 j
.method public hidebysig static void vijay() il managed
{
.entrypoint
ldsfld     int32 zzz::j
ldc.i4.s   16
cgt
ldc.i4.0
ceq
stsfld     bool zzz::i
ldsfld     bool zzz::i
call void [mscorlib]System.Console::WriteLine(bool)
ret
}
.method public hidebysig specialname rtspecialname static void .cctor() il managed
{
ldc.i4.s   19
stsfld int32 zzz::j
ret
}
}

Output

False

“小於等於”(<=)和“大於等於 ”(>=)的實現有一點復雜。實際上它們都具有2個融合在一起的條件。

在>=的情況 中,IL首先使用cgt指令來檢查第1個數字是否大於第2個數字。如果是,它將返回值1,否則就返回值0。 如果第1個條件是FALSE,那麼ceq指令將會檢查這2個數字是否相等。如果相等,它就返回TRUE,否則就返 回FALSE。

讓我們從一個稍微不同的角度來分析上面的IL代碼。我們對這兩個值19和16進行比較。 在這個例子中,因為19大於16,所以指令cgt將會把值1放到棧上。

讓我們在靜態構造函數中將字 段j的值修改為1。現在,由於數字1不大於16,所以cgt指令將把值FALSE或0放到棧上。此後,使用ldc指 令把另一個0放到棧上。現在當ceq指令比較兩個值時,因為它們都是0,所以它會返回TRUE。

現在 ,如果我們將j修改為16,cgt指令將返回FALSE,因為16不大於16。此後,由於使用ldc指令把值0放到棧 上,這兩個傳遞到ceq的值將都是0。由於0與0是相等的,所以返回值是1或TRUE。

如果你不能理解 上面的解釋,那麼就從源代碼中移除ldc.i4.0和ceq這兩行,並觀察輸出。

a.cs

class zzz
{
static bool i;
static int j = 19;
public static void Main()
{
i = j != 16;
System.Console.WriteLine(i);
}
}

a.il

.assembly mukhi {}
.class private auto ansi zzz extends System.Object
{
.field private static bool i
.field private static int32 j
.method public hidebysig static void vijay() il managed
{
.entrypoint
ldsfld     int32 zzz::j
ldc.i4.s   16
ceq
ldc.i4.0
ceq
stsfld     bool zzz::i
ldsfld     bool zzz::i
call void [mscorlib]System.Console::WriteLine(bool)
ret
}
.method public hidebysig specialname rtspecialname static void .cctor() il managed
{
ldc.i4.s   19
stsfld int32 zzz::j
ret
}
}

Output

True

“不等於”操作符,也就是!=,是==的相反操作 。它使用了兩個ceq指令。第1個ceq指令用來檢查棧上的值是否相等。如果它們是相等的,它就會返回 TRUE;否則就返回FALSE。

第2個ceq將前面的ceq結果和FLASE進行比較。如果第1個ceq的結果是 TRUE,那麼最後的答案就是FALSE,反之亦然。

這確實是一種獨創的方式來對一個值求否。

a.cs

class zzz
{
static int i = 1;
public static void Main()
{
while ( i <= 2)
{
System.Console.WriteLine(i);
i++;
}
}
}

a.il

.assembly mukhi {}
.class private auto ansi zzz extends System.Object
{
.field private static int32 i
.method public hidebysig static void vijay() il managed
{
.entrypoint
br.s IL_0018
IL_0002: ldsfld     int32 zzz::i
call void [mscorlib]System.Console::WriteLine(int32)
ldsfld int32 zzz::i
ldc.i4.1
add
stsfld int32 zzz::i
IL_0018: ldsfld int32 zzz::i
ldc.i4.2
ble.s IL_0002
ret
}
.method public hidebysig specialname rtspecialname static void .cctor() il managed
{
ldc.i4.s   1
stsfld int32 zzz::i
ret
}
}

Output

1

2

在介紹完條件語句之後,我們現在將關注於while循環 。這種轉換是必須的,因為我們在諸如while這樣的循環中使用條件語句。包括條件的while循環稍微有點 復雜。

讓我們直接到標號IL_0018上,它位於IL代碼中zzz函數的結尾。這裡存在著一個條件。i的 值(即1)被存儲到棧上。接下來,常量2被放到棧上。

如果你再次訪問C#代碼,那麼while語句中 的條件就是i <= 2。指令ble.s是基於兩個構造函數的:cgt和brfalse。這個指令檢查了第1個值(即 變量i)是否小於等於第2個值。如果是,它就會指示程序跳轉到標號IL_0002。如果不是,程序就移動到 下一個指令。

因此,像ble這樣的指令使我們的工作更加容易,因為我們不必再次使用cgt和 brfalse指令。

在C#中,while結構的條件出現在頂部,但是條件的代碼出現在底部。在轉換為IL 時,在while結構體之間被執行的代碼,會被放置在條件代碼之上。

a.cs

class zzz
{
static int i = 1;
public static void Main()
{
for ( i = 1; i <= 2 ; i++)
{
System.Console.WriteLine(i);
}
}
}

a.il

.assembly mukhi {}
.class private auto ansi zzz extends System.Object
{
.field private static int32 i
.method public hidebysig static void vijay() il managed
{
.entrypoint
ldc.i4.1
stsfld     int32 zzz::i
br.s       IL_001e
IL_0008: ldsfld     int32 zzz::i
call       void [mscorlib]System.Console::WriteLine(int32)
ldsfld     int32 zzz::i
ldc.i4.1
add
stsfld int32 zzz::i
IL_001e: ldsfld     int32 zzz::i
ldc.i4.2
ble.s IL_0008
ret
}
.method public hidebysig specialname rtspecialname static void .cctor() il managed
{
ldc.i4.s   1
stsfld int32 zzz::i
ret
}
}

Output

1

2

老生常談,while和for結構提供了相同的功能,可以互 相轉換。

在for循環中,第1個分號之前的代碼只能被執行一次。因此,將要被初始化的變量i,位 於循環的外面。然後,我們無條件跳轉到標號IL_001e,以檢查i的值是否小於2。如果結果為TRUE,那麼 代碼跳轉到標號IL_0008,它是for語句這段代碼的開始點。

使用WriteLine函數打印出i的值。此 後,變量i的值每次增長1,而條件會被一次又一次的檢查。

a.cs

public class zzz
{
public static void Main()
{
int i;
i = 1;
while ( i <= 2)
{
System.Console.Write(i);
i++;
}
i = 1;
do
{
System.Console.Write(i);
i++;
} while ( i <= 2);
}
}

a.il

.assembly mukhi {}
.class private auto ansi zzz extends [mscorlib]System.Object
{
.method public hidebysig static void vijay() il managed
{
.entrypoint
.locals (int32 V_0)
ldc.i4.1
stloc.0
br.s       IL_000e
IL_0004:  ldloc.0
call       void [mscorlib]System.Console::Write(int32)
ldloc.0
ldc.i4.1
add
stloc.0
IL_000e: ldloc.0
ldc.i4.2
ble.s      IL_0004
ldc.i4.1
stloc.0
IL_0014: ldloc.0
call       void [mscorlib]System.Console::Write(int32)
ldloc.0
ldc.i4.1
add
stloc.0
ldloc.0
ldc.i4.2
ble.s      IL_0014
ret
}
}

Output

1212

在C#中,do循環和while循環之間的區別是——條 件在什麼位置被檢查。

l 在do-while循環中,條件會在循環的結尾被檢查。這意味著循環中的代 碼至少會被調用一次。

l 在while循環中,條件會在循環的開始被檢查。因此,代碼可能從來都不 會被執行。

不管是哪種情況,我們都把值1放到棧上,並初始化變量i或V_1。

l 在while循 環中,我們首先跳轉到標號IL_000e,也就是檢查條件——變量是否小於等於2—— 的地方。如果返回TRUE,我們就跳轉到標號IL_0004。

l 在do-while循環中,首先Write函數會被 執行,隨後包括在花括號{}中的剩余代碼將會被執行。當到達花括號{}中代碼的最後一行時,條件才會被 檢查。

因此,在IL中寫一個do-while循環要比寫一個while循環簡單得多,因為條件會直接在循環 的末尾被檢查。

a.cs

public class zzz
{
public static void Main()
{
int i ;
for ( i = 1; i<= 10 ; i++)
{
if ( i == 2)
break;
System.Console.WriteLine(i);
}
}
}

a.il

.assembly mukhi {}
.class private auto ansi zzz extends [mscorlib]System.Object
{
.method public hidebysig static void vijay() il managed
{
.entrypoint
.locals (int32 V_0)
ldc.i4.1
stloc.0
br.s       IL_0014
IL_0004: ldloc.0
ldc.i4.2
bne.un.s   IL_000a
br.s       IL_0019
IL_000a: ldloc.0
call       void [mscorlib]System.Console::WriteLine(int32)
ldloc.0
ldc.i4.1
add
stloc.0
IL_0014: ldloc.0
ldc.i4.s   10
ble.s      IL_0004
IL_0019:  ret
}
}

Output

1

break語句強迫退出for循環、while循環或do-while循環。和往 常一樣,我們跳轉到標號IL_0014,也就是變量V_0或i的值被放置到棧上的地方。然後,我們把條件值10 放到棧上,並使用ble.s指令檢查i是小於還是大於10。

如果小於,我們就進入到循環中的標號 IL_0004。我們再次把變量i的值放到棧上,並把if語句的值2放到棧上。然後,我們使用bne指令 ——它是ceq指令和brfalse指令的組合。

如果變量V_0為TRUE,break語句就能確保 ——使用br.s指令跳轉到標號IL_0019處的ret語句從而退出loop循環。

a.cs

public class zzz
{
public static void Main()
{
int i ;
for ( i = 1; i<= 10 ; i++)
{
if ( i == 2)
continue;
System.Console.WriteLine(i);
}
}
}

a.il

.assembly mukhi {}
.class private auto ansi zzz extends [mscorlib]System.Object
{
.method public hidebysig static void vijay() il managed
{
.entrypoint
.locals (int32 V_0)
ldc.i4.1
stloc.0
br.s       IL_0014
IL_0004: ldloc.0
ldc.i4.2
bne.un.s   IL_000a
br.s       IL_0010
IL_000a: ldloc.0
call       void [mscorlib]System.Console::WriteLine(int32)
IL_0010: ldloc.0
ldc.i4.1
add
stloc.0
IL_0014: ldloc.0
ldc.i4.s   10
ble.s      IL_0004
ret
}
}

continue語句控制for循環到達結束位置。當if語句結果為TRUE時,程序將繞過 WriteLine函數而跳轉到循環的結束。然後,代碼將在標號IL_0010繼續執行,這裡,變量V_0的值會增加1 。

在break和continue語句之間的主要區別如下所示:

l 在break語句中,程序會跳出循環 。

l 在continue語句中,程序繞過剩下的語句,跳轉到循環的結尾。

goto語句也能到達相 同的功能。從而,break語句、continue語句或goto語句,在轉換為IL時,都會被轉換為相同的br指令。

下面的程序示范了C#中的goto語句會被直接轉換為IL中的br語句。

a.cs

public class zzz
{
public static void Main()
{
goto aa;
aa: ;
}
}

a.il

.assembly mukhi {}
.class private auto ansi zzz extends [mscorlib]System.Object
{
.method public hidebysig static void vijay() il managed
{
.entrypoint
br.s       IL_0002
IL_0002: ret
}
}

C#中一個簡單的goto語句,會被轉換為IL中的br指令。在C#這樣的語言中使用goto被認 為是不恰當的,但是,它在IL中的等價物——br指令,對於實現諸如if語句、循環等各種結構 而言,卻是極其有用的。因此,在編程語言中的禁忌,在IL中卻是極其有用的。

a.cs

public class zzz
{
public static void Main()
{
int j;
for ( int i = 1; i <= 2 ; i++)
System.Console.Write(i);
}
}

a.il

.assembly mukhi {}
.class private auto ansi zzz extends [mscorlib]System.Object
{
.method public hidebysig static void vijay() il managed
{
.entrypoint
.locals (int32 V_0,int32 V_1)
ldc.i4.1
stloc.1
br.s       IL_000e
IL_0004: ldloc.1
call       void [mscorlib]System.Console::Write(int32)
ldloc.1
ldc.i4.1
add
stloc.1
IL_000e: ldloc.1
ldc.i4.2
ble.s      IL_0004
ret
}
}

Output

12

這個例子解釋了for循環。我們在Main函數中創建了一個變量j ,在for語句中創建了一個變量i。在C#中,這個變量i只在for循環中是可見的。因此,這個變量的作用域 是受限制的。

但是,轉換到IL時,所有的變量都具有相同的作用域。這是因為,變量作用域的概 念對IL而言有所不同。因此,取決於C#編譯器所執行的變量作用域規則。因此,我們能判斷出 ——所有的變量在IL中具有相同的作用域和可見性。

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