我們創建了一個接口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