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

《C# to IL》第四章 關鍵字和操作符(下)

編輯:關於C#

我們創建了一個接口iii,它只有一個名為pqr的函數。然後,類yyy實現了接口iii,但是沒有實現函 數pqr,而是添加了一個名為abc的函數。在入口點函數vijay中,函數pqr會被接口iii調用。

我們 之所以沒有得到任何錯誤,是因為override指令的存在。這個指令通知編譯器重定向對接口iii的函數pqr 以及對類yyy的函數abc的任何調用。編譯器對override指令是非常嚴格的。可以從這樣的事實中對此進行 考量——如果在類yyy的定義中沒有實現iii,那麼我們就會得到下列異常:

Output

Exception occurred: System.TypeLoadException: Class yyy tried to override method pqr but does not implement or inherit that methods.

at zzz.vijay()

析構函數

a.cs

class zzz
{
public static void Main()
{
}
~zzz()
{
System.Console.WriteLine("hi");
}
}

a.il

.assembly mukhi {}
.class private auto ansi zzz extends [mscorlib]System.Object
{
.method public hidebysig static void vijay() il managed
{
.entrypoint
ret
}
.method family hidebysig virtual instance void Finalize() il managed
{
ldstr      "hi"
call void [mscorlib]System.Console::WriteLine(class System.String)
ldarg.0
call        instance void [mscorlib]System.Object::Finalize()
ret
}
}

No output

析構函數被轉換為Finalize函數。在C#文檔中也制定了這條信息。 Finalize函數的調用源於Object。文本"hi"不會顯示,因為只要運行時決定了,這個函數就會 被調用。我們所知道的全部是——在對象“死亡”時Finalize就會被調用。因此, 無論何時一個對象“死亡”,它都會調用Finalize。沒有辦法銷毀任何事物,包括.NET對象在 內。

a.cs

class zzz
{
public zzz()
{
}
public zzz(int i)
{
}
public static void Main()
{
}
~zzz()
{
System.Console.WriteLine("hi");
}
}
class yyy : zzz
{
}

a.il

.class private auto ansi yyy extends zzz
{
.method public hidebysig specialname rtspecialname instance void .ctor() il managed
{
ldarg.0
call instance void zzz::.ctor()
ret
}
}

在上 面的代碼中,我們只顯示了類yyy。即使我們有2個構造函數和1個析構函數,類yyy只接收默認的無參構造 函數。因此,派生類不會從基類中繼承構造函數和析構函數。

a.il

.assembly mukhi {}
.class private auto ansi zzz extends [mscorlib]System.Object
{
.method public hidebysig static void vijay() il managed
{
.entrypoint
call void yyy::abc()
ret
}
}
.class private auto ansi yyy extends [mscorlib] System.Array
{
.method public hidebysig static void abc() il managed
{
ldstr "hi"
call void [mscorlib]System.Console::WriteLine(class System.String)
ret
}
}

Output

hi

在C#中,不允許我們 從像System.Array這樣的類中派生一個類,在IL中沒有這樣的約束。因此,上面的代碼不會生成任何錯誤 。

我們確實能夠推斷出C#編譯器具有上面的約束而IL的約束則比較少。一門語言的規則是由編譯 器在編譯期間決定的。

需要說明的是,在C#中,有一些類,是我們不能從中派生的 ——Delegate、Enum和ValueType。

a.il

.assembly mukhi {}
.class private auto ansi zzz extends [mscorlib]System.Object
{
.method public hidebysig static void vijay() il managed
{
.entrypoint
.locals (class aa V_0)
newobj instance void aa::.ctor()
stloc.0
ret
}
}
.class public auto ansi aa extends bb
{
.method public hidebysig specialname rtspecialname instance void .ctor() il managed
{
ldarg.0
call instance void bb::.ctor()
ldstr "aa"
call void [mscorlib]System.Console::WriteLine (class System.String)
ret
}
}
.class public auto ansi bb extends cc
{
.method public hidebysig specialname rtspecialname instance void .ctor() il managed
{
ldarg.0
call       instance void cc::.ctor()
ldstr       "bb"
call       void [mscorlib]System.Console::WriteLine(class System.String)
ret
}
}
.class public auto ansi cc extends aa
{
.method public hidebysig specialname rtspecialname instance void .ctor() il managed
{
ldarg.0
call       instance void aa::.ctor()
ldstr      "cc"
call       void [mscorlib]System.Console::WriteLine(class System.String)
ret
}
}

Error

Exception occurred: System.TypeLoadException: Could not load class 'aa' because the format is bad (too long?)

at zzz.vijay()

在C#中,循環引用是禁止的。編譯器會檢查循環引用,並且如果發 現了它,就會報告一個錯誤。然而,IL並不檢查循環引用,因為Microsoft不希望所有的程序員都使用純 的IL。

因此,類aa繼承自類bb,類bb繼承自類cc,最後類cc又繼承自類aa。這就形成了一個循環 引用。在運行時拋出的異常不會給出循環引用的任何跡象。從而,如果我在這裡沒有為你揭示這個秘密, 那麼這個異常就可能讓你感到困惑。我並不打算顯擺對理解IL有多深這樣的事實,但是偶爾給出一些提示 信息是無妨的。

a.cs

internal class zzz
{
public static void Main()
{
}
}

a.il

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

訪問修飾符,如關鍵字internal,只是C#詞法的一部分,而與IL沒有任何關系。關鍵字internal表示這個 特定的類只能在它所在的文件中被訪問到。

因此,通過掌握IL,我們能夠區分.NET核心和C#領域 存在的特性之間的不同。

a.il

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

在C#中,有一條規則:基類的可訪問性要大於派生類。這 條規則在IL中不適用。從而,即使基類xxx是私有的而派生類yyy是公共的,也不會在IL中生成任何錯誤。

a.il

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

在C#中,一個函數的可訪問 性不能大於它所在類的可訪問性。函數vijay是公有的,然而它所在的類卻是私有的。因此,這個類對包 含在它內部的函數具有更多的約束。再說一遍,在IL中沒有強加這樣的約束。

a.cs

class zzz
{
public static void Main()
{
yyy a = new yyy();
xxx b = new xxx();
a = b;
b = (xxx) a;
}
}
class yyy
{
}
class xxx : yyy
{
}

a.il

.assembly mukhi {}
.class private auto ansi zzz extends [mscorlib]System.Object
{
.method public hidebysig static void vijay() il managed
{
.entrypoint
.locals (class yyy V_0,class xxx V_1)
newobj     instance void yyy::.ctor()
stloc.0
newobj     instance void xxx::.ctor()
stloc.1
ldloc.1
stloc.0
ldloc.0
castclass xxx
stloc.1
ret
}
}
.class private auto ansi yyy extends [mscorlib]System.Object
{
}
.class private auto ansi xxx extends yyy
{
.method public hidebysig specialname rtspecialname instance void .ctor() il managed
{
ldarg.0
call       instance void yyy::.ctor ()
ret
}
}

如果在xxx中沒有構造函數,那麼就會拋出下列異常:

Output

Exception occurred: System.InvalidCastException: An exception of type System.InvalidCastException was thrown.

at zzz.vijay()

在上面的例子中,我們創建 了2個對象a和b,它們分別是類yyy和xxx的實例。類xxx是派生類而yyy是基類。我們能寫出a=b,如果我們 使一個派生類和一個基類相等,那麼就會生成一個錯誤。因此,就需要一個轉換操作符。

在C#中 ,cast會被轉換為castclass指令,後面緊跟著派生類的名稱,也就是要被轉換到的類。如果它不能被轉 換,就會觸發上面提到的異常。

在上面的代碼中,沒有構造函數,從而,就會生成異常。

因此,IL具有大量高級的用來處理對象和類的准則。

a.il

.assembly mukhi {}
.class private auto ansi zzz extends [mscorlib]System.Object
{
.method public hidebysig static void vijay() il managed
{
.entrypoint
.locals (class yyy V_0,class xxx V_1)
newobj     instance void yyy::.ctor()
stloc.0
newobj      instance void xxx::.ctor()
stloc.1
ldloc.1
stloc.0
ldloc.0
castclass xxx
stloc.1
ret
}
}
.class private auto ansi yyy extends [mscorlib]System.Object
{
}
.class private auto ansi xxx extends [mscorlib]System.Object
{
.method public hidebysig specialname rtspecialname instance void .ctor() il managed
{
ldarg.0
call instance void System.Object::.ctor()
ret
}
}

在上面的例子中,類xxx不再從類yyy 中派生。它們都是從Object類中派生的。但是,我們可以把類yyy轉換為類xxx。在帶有構造函數的類xxx 中不會生成任何錯誤,但是如果移除了這個構造函數,就會生成異常。IL還具有它自己的獨特工作方式。

a.il

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

文檔非常清晰地表示了一個密閉類不能被進一步擴展或子類 化。在這個例子中,我們希望看到一個錯誤但是什麼也不會生成。必須提醒你的是,我們現在使用的是 beta版本。下一個版本可能會生成一個錯誤。

a.il

.assembly mukhi {}
.class private auto ansi zzz extends [mscorlib]System.Object
{
.method public hidebysig static void vijay() il managed
{
.entrypoint
.locals (class yyy V_0)
newobj     instance void yyy::.ctor()
stloc.0
ret
}
}
.class private auto ansi abstract yyy
{
}

抽象類不能被直接使用。只 能從中派生。上面的代碼應該生成一個錯誤,但並不是這樣。

a.cs

public class zzz
{
const int i = 10;
public static void Main()
{
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
ldc.i4.s   10
call void [mscorlib]System.Console::WriteLine(int32)
ret
ret
}
}

Output

10

常量是只存在於編譯期間的一個實體。它在運行期間是不可 見的。這就證實了編譯器會移除對編譯期間對象的所有跟蹤。在轉換到IL的過程中,在C#中出現的所有 int i都會被數字10取代。

a.cs

public class zzz
{
const int i = j + 4;
const int j = k - 1;
const int k = 3;
public static void Main()
{
System.Console.WriteLine(k);
}
}

a.il

.assembly mukhi {}
.class private auto ansi zzz extends [mscorlib]System.Object
{
.field private static literal int32 i = int32(0x00000006)
.field private static literal int32 j = int32(0x00000002)
.field private static literal int32 k = int32 (0x00000003)
.method public hidebysig static void vijay() il managed
{
.entrypoint
ldc.i4.3
call void [mscorlib]System.Console::WriteLine(int32)
ret
}
}

Ouput

3

所有的常量都是由編譯器計算的,即使它們 可能關聯到其它常量,但它們會被設定為一個絕對的值。IL運行時不會為文本字段分配任何內存。這涉及 到元數據的領域,稍後我們將對其分析。

a.il

.assembly mukhi {}
.class private auto ansi zzz extends [mscorlib]System.Object
{
.field private static literal int32 i = int32(0x00000006)
.method public hidebysig static void vijay() il managed
{
.entrypoint
ldc.i4.6
stsfld int32 zzz::i
ret
}
}

Output

Exception occurred: System.MissingFieldException: zzz.i

at zzz.vijay()

文本字段表示一個常量值。在IL中,不允許訪問任何文本字段。編 譯器在編譯期間不會生成任何錯誤,但是在運行期間會拋出一個異常。我們希望一個編譯期間錯誤,因為 我們在指令stsfld中使用了一個文本字段。

a.cs

public class zzz
{
public static readonly int i = 10;
public static void Main()
{
System.Console.WriteLine(i);
}
}

a.il

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

Output

10

只讀字段不能被修改。在IL中 ,有一個名為initonly的修飾符,它實現了相同的概念。

a.il

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

文檔非常清晰地表明了只讀字段只能在構造函數中改變,但是CLR不會嚴格地對此進行 檢查。可能在下一個版本,他們應該注意這樣的事情。

因此,在readonly上的全部約束,必須由 (將源代碼轉換為IL的)程序語言強制執行。我們沒有試圖在IL上運行,但是IL希望有人在這種情形中進 行錯誤檢查。

a.cs

public class zzz
{
public static void Main()
{
zzz a = new zzz();
pqr();
a.abc();
}
public static void pqr()
{
}
public void abc()
{
}
}

a.il

.assembly mukhi {}
.class private auto ansi zzz extends [mscorlib]System.Object
{
.field public static initonly int32 i
.method public hidebysig static void vijay() il managed
{
.entrypoint
.locals (class zzz V_0)
newobj instance void zzz::.ctor()
stloc.0
call void zzz::pqr()
ldloc.0
call instance void zzz::abc()
ret
}
.method public hidebysig static void pqr() il managed
{
ret
}
.method public hidebysig instance void abc() il managed
{
ret
}
}

這個例子是一個更新過的版本 。靜態函數pqr不會傳遞這個指針到棧上,但是,非靜態函數abc會把這個指針或引用傳遞到它的變量存儲 在內存中的位置。

因此,在調用函數abc之前,指令ldloc.0會把zzz的引用放到棧上。

a.cs

public class zzz
{
public static void Main()
{
pqr(10,20);
}
public static void pqr(int i , int j)
{
}
}

a.il

.assembly mukhi {}
.class private auto ansi zzz extends [mscorlib]System.Object
{
.field public static initonly int32 i
.method public hidebysig static void vijay() il managed
{
.entrypoint
ldc.i4.s   10
ldc.i4.s   20
call void zzz::pqr(int32,int32)
ret
}
.method public hidebysig static void pqr(int32 i,int32 j) il managed
{
ret
}
}

調用約定指出了這些參數應該被放到棧上的順序。在IL中默認的順序是它們被寫入的順序 。因此,數字10會首先進棧,之後是數字20。

Microsoft實現了相反的順序。從而,20會首先進棧 ,之後是10。我們不能推論出這個特性。

a.cs

public class zzz
{
public static void Main()
{
bb a = new bb();
}
}
public class aa
{
public aa()
{
System.Console.WriteLine("in const aa");
}
public aa(int i)
{
System.Console.WriteLine("in const aa" + i);
}
}
public class bb : aa
{
public bb() : this(20)
{
System.Console.WriteLine("in const bb");
}
public bb(int i) : base(i)
{
System.Console.WriteLine("in const bb" + i);
}
}

a.il

.assembly mukhi {}
.class private auto ansi zzz extends [mscorlib]System.Object
{
.method public hidebysig static void vijay() il managed
{
.entrypoint
.locals (class bb V_0)
newobj instance void bb::.ctor()
stloc.0
ret
}
}
.class public auto ansi aa extends [mscorlib]System.Object
{
.method public hidebysig specialname rtspecialname instance void .ctor() il managed
{
ldarg.0
call       instance void [mscorlib]System.Object::.ctor()
ldstr      "in const aa"
call        void [mscorlib]System.Console::WriteLine(class System.String)
ret
}
.method public hidebysig specialname rtspecialname instance void .ctor(int32 i) il managed
{
ldarg.0
call       instance void [mscorlib] System.Object::.ctor()
ldstr      "in const aa"
ldarga.s   i
box        [mscorlib]System.Int32
call       class System.String [mscorlib]System.String::Concat(class System.Object,class System.Object)
call void [mscorlib]System.Console::WriteLine(class System.String)
ret
}
}
.class public auto ansi bb extends aa
{
.method public hidebysig specialname rtspecialname instance void .ctor() il managed
{
ldarg.0
ldc.i4.s   20
call       instance void bb::.ctor(int32)
ldstr      "in const bb"
call       void [mscorlib]System.Console::WriteLine(class System.String)
ret
}
.method public hidebysig specialname rtspecialname instance void .ctor(int32 i) il managed
{
ldarg.0
ldarg.1
call        instance void aa::.ctor(int32)
ldstr      "in const bb"
ldarga.s    i
box        [mscorlib]System.Int32
call       class System.String [mscorlib]System.String::Concat(class System.Object,class System.Object)
call       void [mscorlib]System.Console::WriteLine(class System.String)
ret
}
}

Output

in const aa20

in const bb20

in const bb

我們只創建了一個對象,它是類bb的一個實例。有3個構造函數會被調用,而不是2個構造函數 (一個是基類的,一個是派生類的)。

l 在IL中,首先,會調用沒有參數的構造函數。

l 然後,當到達構造函數bb時,就會對相同類的另一個帶有參數值20的構造函數進行調用。This(20)會被轉 換為對一個實際的帶有一個參數的構造函數的調用。

l 現在,我們轉移到bb的一個構造函數上。 這裡,初始化對aa的一個構造函數的調用,被作為需要首先被調用的基類的構造函數。

幸運的是 ,aa的基類構造函數不會使我們徒勞無功。在它完成執行之後,就會顯示這個字符串,而最後,bb的無參 構造函數會被調用。

因此,base和this在IL中是不存在的,它們是編譯期間被硬編譯到IL代碼中 的“赝品”。

a.il

.assembly mukhi {}
.class private auto ansi zzz extends [mscorlib]System.Object
{
.method public hidebysig static void vijay() il managed
{
.entrypoint
.locals (class aa V_0)
newobj instance void aa::.ctor()
ret
}
}
.class public auto ansi aa extends [mscorlib] System.Object
{
.method private hidebysig specialname rtspecialname instance void .ctor() il managed
{
ret
}
}

Output

Exception occurred: System.MethodAccessException: aa..ctor()

at zzz.vijay()

我們不能在類的 外部訪問它的私有成員。因此,正如我們在類bb中創建唯一的私有構造函數那樣,我們不能創建任何看上 去像類bb的對象。在C#中,同樣的規則也適用於訪問修飾符。

a.cs

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

a.il

.assembly mukhi {}
.class public auto ansi zzz extends [mscorlib]System.Object
{
.method public hidebysig static void vijay() il managed
{
.entrypoint
.locals (class yyy V_0)
newobj     instance void yyy::.ctor()
stloc.0
ret
}
}
.class private auto ansi yyy extends [mscorlib]System.Object
{
.field public int32 i
.field public bool j
.method public hidebysig specialname rtspecialname instance void .ctor() il managed
{
ldarg.0
call       instance void [mscorlib] System.Object::.ctor()
ldarg.0
ldfld      int32 yyy::i
call       void [mscorlib]System.Console::WriteLine(int32)
ldarg.0
ldfld bool yyy::j
call void [mscorlib]System.Console::WriteLine(bool)
ret
}
}

Output

0

False

這裡,變量i和j沒有被初始化。因此,這些字段沒 有在類yyy的靜態函數中被初始化。在類yyy的任何代碼被調用之前,這些變量會分派到它們的默認值,它 們依賴於它們的數據類型。在這個例子中,它們是通過int和bool類的構造函數來實現的,因為這些構造 函數會首先被調用。

a.cs

class zzz
{
public static void Main()
{
int i = 10;
string j;
j = i >= 20 ? "hi" : "bye";
System.Console.WriteLine(j);
}
}

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,class System.String V_1)
ldc.i4.s   10
stloc.0
ldloc.0
ldc.i4.s   20
bge.s      IL_000f
ldstr      "bye"
br.s       IL_0014
IL_000f: ldstr      "hi"
IL_0014: stloc.1
ldloc.1
call       void [mscorlib]System.Console::WriteLine(class System.String)
ret
}
}

Output

bye

如果把語句壓縮到單獨的一行中,那麼使用三元操作符會更加 “壯麗”。在C#中,變量i和j在轉換到IL時變成了V_0和V_1。我們首先將變量V_0初始化為10 ,隨後把條件值20放到棧上。

l 如果條件為TRUE,那麼bge.s就會執行到標號IL_0014的跳轉。

l 如果條件為FALSE,那麼程序就會進行到標號IL_000f。

然後,程序進行到WriteLine函 數,並打印出相應的文本。

從最終的IL代碼中,無法解釋原始的C#代碼是否使用一個if語句或? 操作符。C#中的大量操作符,例如三元操作符,都是從C程序語言中借用過來的。

a.cs

class zzz
{
public static void Main()
{
int i = 1, j= 2;
if ( i >= 4 & j > 1)
System.Console.WriteLine("& true");
}
}

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.0
ldc.i4.2
stloc.1
ldloc.0
ldc.i4.4
clt
ldc.i4.0
ceq
ldloc.1
ldc.i4.1
cgt
and
brfalse.s IL_001c
ldstr      "& true"
call       void [mscorlib] System.Console::WriteLine(class System.String)
IL_001c: ret
}
}

C#中的&操作符使if更加復雜。如果條件都是TRUE,那麼它就只返回TRUE;否則,它 就返回FALSE。在IL中沒有&操作符的等價物。因此,會以一種間接方式來實現它,如下所示:

l 首先我們使用ldc指令來把一個常量值當到棧上。

l 接下來,stloc指令初始化變量i和j ,即V_0和V_1。

l 然後,V_0的值被放在棧上。

l 之後,檢查條件的值4。

l 然後 ,條件clt用來檢查棧上的第1個項是否小於第2個。如果是,正如在上面的示例那樣,值1(TRUE)就會被 放到棧上。

l C#中的原始表達式是i >= 4。在IL中,會使用<或clt。

l 然後我們使 用ceq檢查相等性,即=,並把0放到棧上。結果為FALSE。

l 然後我們對j > 1采用相同的規則 。這裡,我們使用cgt而不是clt。cgt操作符的結果是TRUE。

l 這個結果TRUE和前面的結果FALSE 進行AND位運算,最後得到一個FALSE值。

注意到AND指令將返回1,當且僅當這兩個條件都是TURE 。在所有其它的條件中,它將會返回FLASE。

a.cs

class zzz
{
public static void Main()
{
int i = 1, j= 2;
if ( i >= 4 && j > 1)
System.Console.WriteLine("&& true");
}
}

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.0
ldc.i4.2
stloc.1
ldloc.0
ldc.i4.4
blt.s      IL_0016
ldloc.1
ldc.i4.1
ble.s      IL_0016
ldstr      "&& true"
call       void [mscorlib]System.Console::WriteLine (class System.String)
IL_0016: ret
}
}

像&&這樣的操作符 被稱為短路運算符,因為它們只有當第一個條件為True時才會執行第2個條件。我們重復了和先前一樣的 IL代碼,但是現在條件是使用blt.s指令進行檢查的,它是clt和brtrue指令的組合。

如果條件為 FALSE,就會跳轉到標號IL_0016處的ret指令。只有當條件為TRUE時,我們就可以向下進行並檢查第2個條 件。為此,我們使用ble.s指令,它是cgt和brfalse的組合。如果第2個條件為FALSE,我們就像前面一樣 跳轉到ret指令,如果為TRUE,我們就執行WriteLine函數。

&&操作符執行比&快,因 為它只能當第一個條件為TRUE時才會進行到下一步。這樣做,第一個表達式的輸出就會影響到最後的輸出 。

|和||操作符也以類似的方式來表現。

a.cs

class zzz
{
public static void Main()
{
bool x,y;
x = true;
y = false;
System.Console.WriteLine( x ^ y);
x = false;
System.Console.WriteLine( x ^ y);
}
}

a.il

.assembly mukhi {}
.class private auto ansi zzz extends [mscorlib]System.Object
{
.method public hidebysig static void vijay() il managed
{
.entrypoint
.locals (bool V_0,bool V_1)
ldc.i4.1
stloc.0
ldc.i4.0
stloc.1
ldloc.0
ldloc.1
xor
call        void [mscorlib]System.Console::WriteLine(bool)
ldc.i4.0
stloc.0
ldloc.0
ldloc.1
xor
call       void [mscorlib]System.Console::WriteLine(bool)
ret
}
}

Output

True

False

^符號被稱為XOR操作符。 XOR就像一個OR語句,但是有一點不同:OR只有當它的一個操作數為TRUE(其它的操作數為FALSE)時才會 返回TRUE。即使這兩個操作數都是TRUE,它也會返回FALSE。xor是一個IL指令。

!=操作符被轉換 為一組常規的IL指令,即完成一次比較操作,而程序會相應地進入分支。

a.cs

class zzz
{
public static void Main()
{
bool x = true;
System.Console.WriteLine(!x);
}
}

a.il

.assembly mukhi {}
.class private auto ansi zzz extends [mscorlib]System.Object
{
.method public hidebysig static void vijay() il managed
{
.entrypoint
.locals (bool V_0)
ldc.i4.1
stloc.0
ldloc.0
ldc.i4.0
ceq
call void [mscorlib]System.Console::WriteLine(bool)
ret
}
}

 

Output

False

C#中的!操作符會被轉換為TRUE或 FALSE,反之亦然。在IL中,使用指令ceq。這個指令檢查了棧上最後的2個參數。如果它們相同,就返回 TRUE;否則,就返回FALSE。

由於變量x為TRUE,它會被初始化為1。此後,會檢查它是否和0相同 。因為它們是不相等的,結果為0或FALSE。這個結果會被放到棧上。同樣適用的邏輯使x為FALSE。0將會 被放到棧上,並檢查它是否和另一個0相等。由於它們是匹配的,所以最後的答案將會是TRUE。

原 文地址:http://www.vijaymukhi.com/documents/books/ilbook/contents.htm

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