前言:
經(jīng)過前面幾篇的學(xué)習(xí),我們了解到指令的大概分類,如:
參數(shù)加載指令,該加載指令以?Ld 開頭,將參數(shù)加載到棧中,以便于后續(xù)執(zhí)行操作命令。
參數(shù)存儲指令,其指令以 St 開頭,將棧中的數(shù)據(jù),存儲到指定的變量中,以方便后續(xù)使用。
創(chuàng)建實例指令,其指令以 New 開頭,用于在運(yùn)行時動態(tài)生成并初始化對象。
方法調(diào)用指令,該指令以 Call?開頭,用于在運(yùn)行時調(diào)用其它方法。
本篇介紹分支條件指令,該指令通常以?Br、或 B、C 開頭,用于在運(yùn)行分支條件時跳轉(zhuǎn)指令。
分支條件指令介紹:
分支條件指令是在.NET Emit編程中關(guān)鍵的控制流程工具,用于在IL代碼中實現(xiàn)條件判斷和控制轉(zhuǎn)移。
ILGenerator 類提供了一系列方法,用于生成這些分支條件指令,包括條件分支、無條件分支和Switch分支等。
條件分支指令(如brtrue和brfalse)根據(jù)棧頂?shù)牟紶栔禌Q定是否跳轉(zhuǎn)到目標(biāo)標(biāo)簽,而無條件分支指令(如br)則總是進(jìn)行跳轉(zhuǎn)。
Switch分支指令則用于在多個目標(biāo)中選擇一個跳轉(zhuǎn)。
通過比較指令(如ceq、cgt和clt),還可以進(jìn)行數(shù)值比較并根據(jù)比較結(jié)果執(zhí)行相應(yīng)的跳轉(zhuǎn)操作。
這些指令的靈活運(yùn)用可以實現(xiàn)復(fù)雜的控制邏輯,例如條件判斷、循環(huán)和異常處理等。
深入理解分支條件指令將幫助開發(fā)者更好地掌握.NET Emit編程,提高代碼生成的效率和靈活性。
常用分支條件指令:
條件跳轉(zhuǎn)指令:
-
beq
:如果兩個值相等,則跳轉(zhuǎn)到指定的標(biāo)簽。 -
bge
:如果第一個值大于或等于第二個值,則跳轉(zhuǎn)到指定的標(biāo)簽。 -
bgt
:如果第一個值大于第二個值,則跳轉(zhuǎn)到指定的標(biāo)簽。 -
ble
:如果第一個值小于或等于第二個值,則跳轉(zhuǎn)到指定的標(biāo)簽。 -
blt
:如果第一個值小于第二個值,則跳轉(zhuǎn)到指定的標(biāo)簽. -
bne.un
:如果兩個無符號整數(shù)值不相等,則跳轉(zhuǎn)到指定的標(biāo)簽。 -
brtrue
:如果值為 true,則跳轉(zhuǎn)到指定的標(biāo)簽。 -
brfalse
:如果值為 false,則跳轉(zhuǎn)到指定的標(biāo)簽。 -
brtrue.s
:如果值為 true,則跳轉(zhuǎn)到指定的標(biāo)簽(短格式)。 -
brfalse.s
:如果值為 false,則跳轉(zhuǎn)到指定的標(biāo)簽(短格式).
無條件跳轉(zhuǎn)指令:
-
br
:無條件跳轉(zhuǎn)到指定的標(biāo)簽。 -
br.s
:短格式的無條件跳轉(zhuǎn)到指定的標(biāo)簽。 -
leave
:無條件跳轉(zhuǎn)到 try、filter 或 finally 塊的末尾。 -
leave.s
:短格式的無條件跳轉(zhuǎn)到 try、filter 或 finally 塊的末尾.
比較跳轉(zhuǎn)指令:
-
bgt.un
:如果第一個無符號整數(shù)值大于第二個值,則跳轉(zhuǎn)到指定的標(biāo)簽。 -
bge.un
:如果第一個無符號整數(shù)值大于或等于第二個值,則跳轉(zhuǎn)到指定的標(biāo)簽。 -
blt.un
:如果第一個無符號整數(shù)值小于第二個值,則跳轉(zhuǎn)到指定的標(biāo)簽。 -
ble.un
:如果第一個無符號整數(shù)值小于或等于第二個值,則跳轉(zhuǎn)到指定的標(biāo)簽.
其他跳轉(zhuǎn)指令:
-
switch
:根據(jù)給定的索引值跳轉(zhuǎn)到不同的標(biāo)簽。 -
brnull
:如果值為 null,則跳轉(zhuǎn)到指定的標(biāo)簽。 -
brinst
:如果對象是類的實例,則跳轉(zhuǎn)到指定的標(biāo)簽。
這些指令可以幫助控制流程,在特定條件下跳轉(zhuǎn)到指定的標(biāo)簽位置執(zhí)行相應(yīng)的代碼。
從以上分類說明可以看出,該指令需要配置標(biāo)簽使用,對于標(biāo)簽的用法,
如有遺忘,可以回去補(bǔ)一下文章:.NET Emit 入門教程:第六部分:IL 指令:2:詳解 ILGenerator 輔助方法
上面指令按使用方式,只分兩種:
1、條件跳轉(zhuǎn)指令:根據(jù)棧頂?shù)臄?shù)據(jù),及指令的判斷條件,來跳轉(zhuǎn)標(biāo)簽。 2、Switch 分支跳轉(zhuǎn)指令:根據(jù)給定的索引值,來跳轉(zhuǎn)標(biāo)簽。
1、條件跳轉(zhuǎn)指令:
條件分支指令是在IL代碼中用于根據(jù)條件來執(zhí)行跳轉(zhuǎn)操作的指令。
它們可以根據(jù)棧頂?shù)模ú紶枺┲?/strong>來決定是否跳轉(zhuǎn)到目標(biāo)標(biāo)簽:
示例指令:Brtrue
示例指令:Brfalse
該 true、false?指令,除了 bool 值,還兼容判斷了空(引用)和數(shù)字(零),這個小細(xì)節(jié)要注意。?
示例指令:Br
示例代碼:
var dynamicMethod = new DynamicMethod("WriteAOrB", typeof(void), new Type[] { typeof(bool) }, typeof(AssMethodIL_Condition)); ILGenerator il = dynamicMethod.GetILGenerator(); var labelEnd = il.DefineLabel(); var labelFalse = il.DefineLabel(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Brfalse_S, labelFalse); il.EmitWriteLine("true."); il.Emit(OpCodes.Br_S, labelEnd); il.MarkLabel(labelFalse); il.EmitWriteLine("false"); il.MarkLabel(labelEnd); il.Emit(OpCodes.Ret); // 返回該值
運(yùn)行結(jié)果:
說明:
1、在 true、false 指令中,通常只會使用其中一個。 2、在分支條件中,很多時候需要配合 Br 無條件指令跳出分支。 3、在定義標(biāo)簽時,除了定義分支標(biāo)簽,還要定義結(jié)束標(biāo)簽,以便 Br 無條件指令的跳出。
4、其它條件指令的使用,和bool條件指令的使用是一樣的,只需要理解指令的含義即可。
2、Switch?分支條件指令:
Switch 分支指令用于在多個目標(biāo)中選擇一個跳轉(zhuǎn),類似于在高級編程語言中的 switch 或者 case 語句。
在IL代碼中,Switch 指令可以實現(xiàn)根據(jù)一個整數(shù)值來決定跳轉(zhuǎn)到不同的目標(biāo)標(biāo)簽。Switch 分支指令的主要指令是 switch,其作用如下:
- switch:?該指令從標(biāo)簽數(shù)組中選擇一個目標(biāo)標(biāo)簽進(jìn)行跳轉(zhuǎn)。在 IL 代碼中,標(biāo)簽數(shù)組通常在 switch 指令之前被定義,并且 switch 指令的操作數(shù)是標(biāo)簽數(shù)組的引用。switch 指令會從操作數(shù)指定的標(biāo)簽數(shù)組中根據(jù)整數(shù)值索引來選擇目標(biāo)標(biāo)簽,然后執(zhí)行跳轉(zhuǎn)操作。
Switch 分支指令的作用是根據(jù)一個整數(shù)值來決定跳轉(zhuǎn)到不同的目標(biāo)標(biāo)簽,這在處理具有多個選擇的情況下非常有用,可以使得代碼更加簡潔和高效。
注意事項:
1、該 Switch?指令只是類似 switch?編程,但不等同。?
2、該 Switch?指令只能根據(jù)索引進(jìn)行指令跳轉(zhuǎn)。
同時,Switch?指令在IL代碼編寫起來,會相對復(fù)雜一點(diǎn),特別是 case 一多,寫起來可會要你命3000.
為了區(qū)分 Switch?指令和我們編寫代碼時的指令 Switch case?區(qū)別,我們來看以下示例:
這一次我們反過來,先寫 C# 代碼,再看它生成的 IL 代碼。
下面給一個示例代碼1:
static void TestString(string c) { switch(c) { case "a": Console.WriteLine("A");break; case "b": Console.WriteLine("B");break; default: Console.WriteLine("C");break; } }
反編繹,看生成的 IL?代碼:
.method private hidebysig static void TestString ( string c ) cil managed { // Method begins at RVA 0x3808 // Code size 73 (0x49) .maxstack 2 .locals init ( [0] string, [1] string ) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.1 IL_0003: ldloc.1 IL_0004: stloc.0 IL_0005: ldloc.0 IL_0006: ldstr "a" IL_000b: call bool [mscorlib]System.String::op_Equality(string, string) IL_0010: brtrue.s IL_0021 IL_0012: ldloc.0 IL_0013: ldstr "b" IL_0018: call bool [mscorlib]System.String::op_Equality(string, string) IL_001d: brtrue.s IL_002e IL_001f: br.s IL_003b IL_0021: ldstr "A" IL_0026: call void [mscorlib]System.Console::WriteLine(string) IL_002b: nop IL_002c: br.s IL_0048 IL_002e: ldstr "B" IL_0033: call void [mscorlib]System.Console::WriteLine(string) IL_0038: nop IL_0039: br.s IL_0048 IL_003b: ldstr "C" IL_0040: call void [mscorlib]System.Console::WriteLine(string) IL_0045: nop IL_0046: br.s IL_0048 IL_0048: ret } // end of method Program::TestString
從該生成的 IL?代碼中,可以看出并沒有 Switch 指令,而是常規(guī)調(diào)用字符串比較后,用 bool 條件指令進(jìn)行跳轉(zhuǎn)。
那是不是用數(shù)字類型就會得到 Switch?指令呢?
再用數(shù)字型來一次:
static void TestInt(int c) { switch (c) { case 1: Console.WriteLine("AAA"); break; case 2: Console.WriteLine("BBB"); break; default: Console.WriteLine("CCC"); break; } }
得到的 IL?代碼如下:
.method private hidebysig static void TestInt ( int32 c ) cil managed { // Method begins at RVA 0x3860 // Code size 57 (0x39) .maxstack 2 .locals init ( [0] int32, [1] int32 ) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.1 IL_0003: ldloc.1 IL_0004: stloc.0 IL_0005: ldloc.0 IL_0006: ldc.i4.1 IL_0007: beq.s IL_0011 IL_0009: br.s IL_000b IL_000b: ldloc.0 IL_000c: ldc.i4.2 IL_000d: beq.s IL_001e IL_000f: br.s IL_002b IL_0011: ldstr "AAA" IL_0016: call void [mscorlib]System.Console::WriteLine(string) IL_001b: nop IL_001c: br.s IL_0038 IL_001e: ldstr "BBB" IL_0023: call void [mscorlib]System.Console::WriteLine(string) IL_0028: nop IL_0029: br.s IL_0038 IL_002b: ldstr "CCC" IL_0030: call void [mscorlib]System.Console::WriteLine(string) IL_0035: nop IL_0036: br.s IL_0038 IL_0038: ret } // end of method Program::TestInt
依舊是 br?跳轉(zhuǎn)指令。
再試一下用枚舉呢?
static void TestEnum(DataAccessKind c) { switch (c) { case DataAccessKind.Read: Console.WriteLine("AAA"); break; case DataAccessKind.None: Console.WriteLine("BBB"); break; default: Console.WriteLine("CCC"); break; } }
得到?IL?代碼如下:
.method private hidebysig static void TestEnum ( valuetype [System.Data]Microsoft.SqlServer.Server.DataAccessKind c ) cil managed { // Method begins at RVA 0x38a8 // Code size 56 (0x38) .maxstack 2 .locals init ( [0] valuetype [System.Data]Microsoft.SqlServer.Server.DataAccessKind, [1] valuetype [System.Data]Microsoft.SqlServer.Server.DataAccessKind ) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.1 IL_0003: ldloc.1 IL_0004: stloc.0 IL_0005: ldloc.0 IL_0006: brfalse.s IL_001d IL_0008: br.s IL_000a IL_000a: ldloc.0 IL_000b: ldc.i4.1 IL_000c: beq.s IL_0010 IL_000e: br.s IL_002a IL_0010: ldstr "AAA" IL_0015: call void [mscorlib]System.Console::WriteLine(string) IL_001a: nop IL_001b: br.s IL_0037 IL_001d: ldstr "BBB" IL_0022: call void [mscorlib]System.Console::WriteLine(string) IL_0027: nop IL_0028: br.s IL_0037 IL_002a: ldstr "CCC" IL_002f: call void [mscorlib]System.Console::WriteLine(string) IL_0034: nop IL_0035: br.s IL_0037 IL_0037: ret } // end of method Program::TestEnum
還是沒有見 Switch?指令。
可見,編程中的Switch,和 IL?的 Switch?指令是不同的。
所以很容易陷入一個誤區(qū),以為代碼用 Switch 寫分支,對應(yīng)的IL就得用 Switch 指令。
下面演示一個用 Switch?指令的示例:
var dynamicMethod = new DynamicMethod("WriteAOrB", typeof(void), new Type[] { typeof(int) }, typeof(AssMethodIL_Condition)); ILGenerator il = dynamicMethod.GetILGenerator(); var labelEnd = il.DefineLabel(); Label labelIndex_0 = il.DefineLabel(); Label labelIndex_1 = il.DefineLabel(); Label labelIndex_2 = il.DefineLabel(); //BreakOp None=-1,Null=0,Empty=1,NullOrEmpty=2 var lables = new Label[] { labelIndex_0, labelIndex_1, labelIndex_2 };//0、1、2 il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Switch, lables); il.MarkLabel(labelIndex_0); il.EmitWriteLine("0."); il.Emit(OpCodes.Br_S, labelEnd); il.MarkLabel(labelIndex_1); il.EmitWriteLine("1."); il.Emit(OpCodes.Br_S, labelEnd); il.MarkLabel(labelIndex_2); il.EmitWriteLine("2."); il.Emit(OpCodes.Br_S, labelEnd); il.MarkLabel(labelEnd); il.Emit(OpCodes.Ret); // 返回該值 dynamicMethod.Invoke(null, new object[] { 1 }); Console.Read();
運(yùn)行結(jié)果:
從上面的示例可以看出,使用 Switch?指令,其實是在 IL?中編寫類似于 Switch 語法的條件分支,而不是和C#?語法的 Switch 對應(yīng)。
總結(jié):
本篇介紹了在IL(Intermediate Language)代碼中常見的兩種指令類型:條件跳轉(zhuǎn)指令和Switch 分支跳轉(zhuǎn)指令。
條件跳轉(zhuǎn)指令則用于執(zhí)行數(shù)值比較操作,根據(jù)比較結(jié)果執(zhí)行相應(yīng)的跳轉(zhuǎn)操作或?qū)⒈容^結(jié)果壓入棧中。
由于其使用方式是一致,因此示例僅展示bool條件指令的使用,沒有對其它指令展開示例,但其它條件指令的含義,也是需要仔細(xì)了解一下的。
Switch 分支跳轉(zhuǎn)指令用于根據(jù)一個整數(shù)值選擇不同的目標(biāo)標(biāo)簽進(jìn)行跳轉(zhuǎn),類似于高級編程語言中的 switch 或者 case 語句。
通過 Switch 指令,可以使代碼更加簡潔、高效,并提高可讀性和可維護(hù)性。在處理具有多個選擇的情況下特別有用,例如枚舉類型或者狀態(tài)機(jī)的處理。
它們在條件分支指令和循環(huán)控制中起著關(guān)鍵作用,通過靈活運(yùn)用比較指令,可以實現(xiàn)各種復(fù)雜的算法和邏輯。文章來源:http://www.zghlxwxcb.cn/news/detail-848945.html
綜上所述,Switch 分支跳轉(zhuǎn)指令和條件跳轉(zhuǎn)指令是IL代碼中常用的兩種控制流指令,它們在編寫和優(yōu)化IL代碼時起著重要作用,能夠使代碼更加簡潔、高效和易于理解。文章來源地址http://www.zghlxwxcb.cn/news/detail-848945.html
到了這里,關(guān)于.NET Emit 入門教程:第六部分:IL 指令:7:詳解 ILGenerator 指令方法:分支條件指令的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!