1. 程式人生 > >通過IL理解C# try catch finally中真正的return 返回值

通過IL理解C# try catch finally中真正的return 返回值

首先,我們先寫一個簡單的tryfinally 語句,來看一下IL反編譯後的程式碼 ,關於IL的一些知識,可以參考這個人的部落格(http://www.cnblogs.com/zery/p/3366175.html)。或者自己百度一下.NET IL 網上會有很多這樣的介紹,這裡本文不在介紹。下面開始介紹:

static int TestIntReturnInTry()
        {
            int i = 0;
            try
            {
                return i;
            }
            finally
{ i = 2; } }

通過IL檢視的效果為:

.method private hidebysig static int32  TestIntReturnInTry() cil managed
{
  // 程式碼大小       16 (0x10)
  /計算棧(Evaluation Stack) 可容納資料項的最大個數
  .maxstack  1
  //定義並初始化引數 並存入 區域性變量表(Record Frame)中 (此時我們會發現 這個區域性變量表中有CS$1$0000 這個其實是真實return 後面的那個變數,這裡我們可以理解為只要return 變數 ,C# 編譯器就會建立兩個變數,一個區域性變數,一個區域性變數的副本,用於真實的返回。我們通過.NET Reflector 可以檢視到,如通過NET Reflector所示(num2其實就是對應的CS$1$0000))
.locals init ([0] int32 i, [1] int32 CS$1$0000) //如果修補操作碼,則填充空間。儘管可能消耗處理週期,但未執行任何有意義的操作。 IL_0000: nop //將整數值 0 作為 int32 推送到計算堆疊上。 IL_0001: ldc.i4.0 //從計算堆疊的頂部彈出當前值並將其儲存到索引 0 處的區域性變數列表中。 IL_0002: stloc.0 .try { //如果修補操作碼,則填充空間。儘管可能消耗處理週期,但未執行任何有意義的操作。 IL_0003: nop //將索引 0 處的區域性變數載入到計算堆疊上。
IL_0004: ldloc.0 //從計算堆疊的頂部彈出當前值並將其儲存到索引 1 處的區域性變數列表中。 IL_0005: stloc.1 //退出受保護的程式碼區域,無條件將控制轉移到目標指令(縮寫形式)。到IL_000d IL_0006: leave.s IL_000d } // end .try finally { //如果修補操作碼,則填充空間。儘管可能消耗處理週期,但未執行任何有意義的操作。 IL_0008: nop //將整數值 2 作為 int32 推送到計算堆疊上。 IL_0009: ldc.i4.2 //從計算堆疊的頂部彈出當前值並將其儲存到索引 0 處的區域性變數列表中。 IL_000a: stloc.0 //如果修補操作碼,則填充空間。儘管可能消耗處理週期,但未執行任何有意義的操作。 IL_000b: nop //finally 結束 IL_000c: endfinally } // end handler //如果修補操作碼,則填充空間。儘管可能消耗處理週期,但未執行任何有意義的操作。 IL_000d: nop //將索引 1 處的區域性變數載入到計算堆疊上。 IL_000e: ldloc.1 // 從當前方法返回,並將返回值(如果存在)從呼叫方的計算堆疊推送到被呼叫方的計算堆疊上。由於上一步(IL_000e) 把索引1處的載入到計算機堆疊上,所以return 返回的就是這個值 即CS$1$0000 IL_000f: ret } // end of method TryRetrunFinally::TestIntReturnInTry

我們簡單來畫一下這個流程(有點粗糙,請讀者見諒):
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
.NET Reflector 檢視反編譯的結果,返回的是num2

private static int TestIntReturnInTry()
{
    int num2;
    int num = 0;
    try
    {
        num2 = num;
    }
    finally
    {
        num = 2;
    }
    return num2;
}

以上就是簡單返回一個int的情況,下面我們來分析返回一個引用型別(這裡使用User案例分析)。

class User
    {
        public string Name { get; set; }
        public DateTime BirthDay { get; set; }
    }
static User TestUserReturnInTry()
        {
            User user = new User()
            {
                Name = "Mike",
                BirthDay = new DateTime(2010, 1, 1)
            };
            try
            {
                return user;
            }
            finally
            {
                user.Name = "Rose";
                user.BirthDay = new DateTime(2010, 2, 2);
                Console.WriteLine("UserName Has changed");
                user = null;
            }
        }

通過IL檢視的程式碼為:

.method private hidebysig static class NowCoderProgrammingProject.User 
        TestUserReturnInTry() cil managed
{
  // 程式碼大小       93 (0x5d)
  /計算棧(Evaluation Stack) 可容納資料項的最大個數
  .maxstack  4    

   //定義並初始化引數 並存入 區域性變量表(Record Frame)中(<>g__initLocal0 這個是編譯器預設生成的一個區域性變數,CS$1$0000 是return 生的一個區域性變數)
  .locals init ([0] class NowCoderProgrammingProject.User user,
           [1] class NowCoderProgrammingProject.User '<>g__initLocal0',
           [2] class NowCoderProgrammingProject.User CS$1$0000)     
  //如果修補操作碼,則填充空間。儘管可能消耗處理週期,但未執行任何有意義的操作。
  IL_0000:  nop
  //建立一個值型別的新物件或新例項,並將物件引用(User 型別)推送到計算堆疊上。
  IL_0001:  newobj     instance void NowCoderProgrammingProject.User::.ctor()
  //從計算堆疊的頂部彈出當前值並將其儲存到索引 1 處的區域性變數列表中。
  IL_0006:  stloc.1 
  //將索引 1 處的區域性變數載入到計算堆疊上。    
  IL_0007:  ldloc.1 
  //推送對元資料中儲存的字串的新物件引用。
  IL_0008:  ldstr      "Mike"
  //  對物件呼叫後期繫結方法,並且將返回值推送到計算堆疊上。
  IL_000d:  callvirt   instance void NowCoderProgrammingProject.User::set_Name(string) 
  //如果修補操作碼,則填充空間。儘管可能消耗處理週期,但未執行任何有意義的操作。
  IL_0012:  nop
  //將索引 1 處的區域性變數載入到計算堆疊上。
  IL_0013:  ldloc.1
  //將所提供的 int32 型別的值作為 int32 推送到計算堆疊上。
  IL_0014:  ldc.i4     0x7da  
  //將整數值 1 作為 int32 推送到計算堆疊上。
  IL_0019:  ldc.i4.1
   //將整數值 1 作為 int32 推送到計算堆疊上。
  IL_001a:  ldc.i4.1
  //生成一個新的DateTime的例項
  IL_001b:  newobj     instance void [mscorlib]System.DateTime::.ctor(int32,
                                                                      int32,
                                                                      int32)
  //呼叫虛方法設定日期
  IL_0020:  callvirt   instance void NowCoderProgrammingProject.User::set_BirthDay(valuetype [mscorlib]System.DateTime)
  IL_0025:  nop
  //將索引 1 處的區域性變數載入到計算堆疊上。
  IL_0026:  ldloc.1
   //從計算堆疊的頂部彈出當前值並將其儲存到索引 0 處的區域性變數列表中。
  IL_0027:  stloc.0
  .try
  {
    IL_0028:  nop
    //將索引 0 處的區域性變數載入到計算堆疊上。
    IL_0029:  ldloc.0
    //從計算堆疊的頂部彈出當前值並將其儲存到索引 2 處的區域性變數列表中。
    IL_002a:  stloc.2
     //退出受保護的程式碼區域,無條件將控制轉移到目標指令(縮寫形式)。到IL_005a
    IL_002b:  leave.s    IL_005a
  }  // end .try
  finally
  {
    IL_002d:  nop
     //將索引 0 處的區域性變數載入到計算堆疊上。
    IL_002e:  ldloc.0
    //推送對元資料中儲存的字串Rose的新物件引用。
    IL_002f:  ldstr      "Rose"
    //呼叫虛方法設定字串Mame的值
    IL_0034:  callvirt   instance void NowCoderProgrammingProject.User::set_Name(string)
    IL_0039:  nop
     //將索引 0 處的區域性變數載入到計算堆疊上。
    IL_003a:  ldloc.0
    //將所提供的 int32 型別的值作為 int32 推送到計算堆疊上。
    IL_003b:  ldc.i4     0x7da (2010)
    //將整數值 2 作為 int32 推送到計算堆疊上。
    IL_0040:  ldc.i4.2
     //將整數值 2 作為 int32 推送到計算堆疊上。
    IL_0041:  ldc.i4.2
    //生成DateTime的新例項
    IL_0042:  newobj     instance void [mscorlib]System.DateTime::.ctor(int32,
                                                                        int32,
                                                                        int32)
    //呼叫許方法設定BirthDay的值                                                                   
    IL_0047:  callvirt   instance void NowCoderProgrammingProject.User::set_BirthDay(valuetype [mscorlib]System.DateTime)
    IL_004c:  nop
      //推送對元資料中儲存的字串UserName Has changed的新物件引用。
    IL_004d:  ldstr      "UserName Has changed"
    //呼叫虛方法進行輸出
    IL_0052:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_0057:  nop
    //將空引用(O 型別)推送到計算堆疊上。
    IL_0058:  ldnull
    //從計算堆疊的頂部彈出當前值並將其儲存到索引 0 處的區域性變數列表中。
    IL_0059:  stloc.0
    IL_005a:  nop
    IL_005b:  endfinally
  }  // end handler
  IL_005c:  nop
  //  將索引 2 處的區域性變數載入到計算堆疊上。
  IL_005d:  ldloc.2
  IL_005e:  ret
} // end of method TryRetrunFinally::TestUserReturnInTry

IL流程圖如下:

這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
通過.NET Reflaector 檢視的反編譯結果為:

private static User TestUserReturnInTry()
{
    User user3;
    User user = new User {
        Name = "Mike",
        BirthDay = new DateTime(0x7da, 1, 1)
    };
    try
    {
        user3 = user;
    }
    finally
    {
        user.Name = "Rose";
        user.BirthDay = new DateTime(0x7da, 2, 2);
        Console.WriteLine("UserName Has changed");
        user = null;
    }
    return user3;
}

完整的程式碼和輸出如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NowCoderProgrammingProject
{
    class TryRetrunFinally
    {
        public static void Main()
        {
            int val = TestIntReturnInTry();
            Console.WriteLine("val:"+val);
            User user = TestUserReturnInTry();
            if (user == null)
            {
                Console.WriteLine("User is null");
            }
            else
            {
                Console.WriteLine("user's name:" + user.Name);
                Console.WriteLine("user's birthday:" + user.BirthDay.ToString("yyyy-MM-dd"));
            }
        }

        static int TestIntReturnInTry()
        {
            int i = 0;
            try
            {
                return i;
            }
            finally
            {
                i = 2;
            }
        }

        static User TestUserReturnInTry()
        {
            User user = new User()
            {
                Name = "Mike",
                BirthDay = new DateTime(2010, 1, 1)
            };
            try
            {
                return user;
            }
            finally
            {
                user.Name = "Rose";
                user.BirthDay = new DateTime(2010, 2, 2);
                Console.WriteLine("UserName Has changed");
                user = null;
            }
        }
    }
    class User
    {
        public string Name { get; set; }
        public DateTime BirthDay { get; set; }
    }
}

輸出結果為下圖:

這裡寫圖片描述

另附.NET 中IL指令表:

名稱 說明
Add 將兩個值相加並將結果推送到計算堆疊上。
Add.Ovf 將兩個整數相加,執行溢位檢查,並且將結果推送到計算堆疊上。
Add.Ovf.Un 將兩個無符號整數值相加,執行溢位檢查,並且將結果推送到計算堆疊上。
And 計算兩個值的按位“與”並將結果推送到計算堆疊上。
Arglist 返回指向當前方法的引數列表的非託管指標。
Beq 如果兩個值相等,則將控制轉移到目標指令。
Beq.S 如果兩個值相等,則將控制轉移到目標指令(短格式)。
Bge 如果第一個值大於或等於第二個值,則將控制轉移到目標指令。
Bge.S 如果第一個值大於或等於第二個值,則將控制轉移到目標指令(短格式)。
Bge.Un 當比較無符號整數值或不可排序的浮點型值時,如果第一個值大於第二個值,則將控制轉移到目標指令。
Bge.Un.S 當比較無符號整數值或不可排序的浮點型值時,如果第一個值大於第二個值,則將控制轉移到目標指令(短格式)。
Bgt 如果第一個值大於第二個值,則將控制轉移到目標指令。
Bgt.S 如果第一個值大於第二個值,則將控制轉移到目標指令(短格式)。
Bgt.Un 當比較無符號整數值或不可排序的浮點型值時,如果第一個值大於第二個值,則將控制轉移到目標指令。
Bgt.Un.S 當比較無符號整數值或不可排序的浮點型值時,如果第一個值大於第二個值,則將控制轉移到目標指令(短格式)。
Ble 如果第一個值小於或等於第二個值,則將控制轉移到目標指令。
Ble.S 如果第一個值小於或等於第二個值,則將控制轉移到目標指令(短格式)。
Ble.Un 當比較無符號整數值或不可排序的浮點型值時,如果第一個值小於或等於第二個值,則將控制轉移到目標指令。
Ble.Un.S 當比較無符號整數值或不可排序的浮點值時,如果第一個值小於或等於第二個值,則將控制權轉移到目標指令(短格式)。
Blt 如果第一個值小於第二個值,則將控制轉移到目標指令。
Blt.S 如果第一個值小於第二個值,則將控制轉移到目標指令(短格式)。
Blt.Un 當比較無符號整數值或不可排序的浮點型值時,如果第一個值小於第二個值,則將控制轉移到目標指令。
Blt.Un.S 當比較無符號整數值或不可排序的浮點型值時,如果第一個值小於第二個值,則將控制轉移到目標指令(短格式)。
Bne.Un 當兩個無符號整數值或不可排序的浮點型值不相等時,將控制轉移到目標指令。
Bne.Un.S 當兩個無符號整數值或不可排序的浮點型值不相等時,將控制轉移到目標指令(短格式)。
Box 將值類轉換為物件引用(O 型別)。
Br 無條件地將控制轉移到目標指令。
Br.S 無條件地將控制轉移到目標指令(短格式)。
Break 向公共語言結構 (CLI) 發出訊號以通知偵錯程式已撞上了一個斷點。
Brfalse 如果 value 為 false、空引用(Visual Basic 中的 Nothing)或零,則將控制轉移到目標指令。
Brfalse.S 如果 value 為 false、空引用或零,則將控制轉移到目標指令。
Brtrue 如果 value 為 true、非空或非零,則將控制轉移到目標指令。
Brtrue.S 如果 value 為 true、非空或非零,則將控制轉移到目標指令(短格式)。
Call 呼叫由傳遞的方法說明符指示的方法。
Calli 通過呼叫約定描述的引數呼叫在計算堆疊上指示的方法(作為指向入口點的指標)。
Callvirt 對物件呼叫後期繫結方法,並且將返回值推送到計算堆疊上。
Castclass 嘗試將引用傳遞的物件轉換為指定的類。
Ceq 比較兩個值。如果這兩個值相等,則將整數值 1 (int32) 推送到計算堆疊上;否則,將 0 (int32) 推送到計算堆疊上。
Cgt 比較兩個值。如果第一個值大於第二個值,則將整數值 1 (int32) 推送到計算堆疊上;反之,將 0 (int32) 推送到計算堆疊上。
Cgt.Un 比較兩個無符號的或不可排序的值。如果第一個值大於第二個值,則將整數值 1 (int32) 推送到計算堆疊上;反之,將 0 (int32) 推送到計算堆疊上。
Ckfinite 如果值不是有限數,則引發 ArithmeticException。
Clt 比較兩個值。如果第一個值小於第二個值,則將整數值 1 (int32) 推送到計算堆疊上;反之,將 0 (int32) 推送到計算堆疊上。
Clt.Un 比較無符號的或不可排序的值 value1 和 value2。如果 value1 小於 value2,則將整數值 1 (int32 ) 推送到計算堆疊上;反之,將 0 ( int32 ) 推送到計算堆疊上。
Constrained 約束要對其進行虛方法呼叫的型別。
Conv.I 將位於計算堆疊頂部的值轉換為 native int。
Conv.I1 將位於計算堆疊頂部的值轉換為 int8,然後將其擴充套件(填充)為 int32。
Conv.I2 將位於計算堆疊頂部的值轉換為 int16,然後將其擴充套件(填充)為 int32。
Conv.I4 將位於計算堆疊頂部的值轉換為 int32。
Conv.I8 將位於計算堆疊頂部的值轉換為 int64。
Conv.Ovf.I 將位於計算堆疊頂部的有符號值轉換為有符號 native int,並在溢位時引發 OverflowException。
Conv.Ovf.I.Un 將位於計算堆疊頂部的無符號值轉換為有符號 native int,並在溢位時引發 OverflowException。
Conv.Ovf.I1 將位於計算堆疊頂部的有符號值轉換為有符號 int8 並將其擴充套件為 int32,並在溢位時引發 OverflowException。
Conv.Ovf.I1.Un 將位於計算堆疊頂部的無符號值轉換為有符號 int8 並將其擴充套件為 int32,並在溢位時引發 OverflowException。
Conv.Ovf.I2 將位於計算堆疊頂部的有符號值轉換為有符號 int16 並將其擴充套件為 int32,並在溢位時引發 OverflowException。
Conv.Ovf.I2.Un 將位於計算堆疊頂部的無符號值轉換為有符號 int16 並將其擴充套件為 int32,並在溢位時引發 OverflowException。
Conv.Ovf.I4 將位於計算堆疊頂部的有符號值轉換為有符號 int32,並在溢位時引發 OverflowException。
Conv.Ovf.I4.Un 將位於計算堆疊頂部的無符號值轉換為有符號 int32,並在溢位時引發 OverflowException。
Conv.Ovf.I8 將位於計算堆疊頂部的有符號值轉換為有符號 int64,並在溢位時引發 OverflowException。
Conv.Ovf.I8.Un 將位於計算堆疊頂部的無符號值轉換為有符號 int64,並在溢位時引發 OverflowException。
Conv.Ovf.U 將位於計算堆疊頂部的有符號值轉換為 unsigned native int,並在溢位時引發 OverflowException。
Conv.Ovf.U.Un 將位於計算堆疊頂部的無符號值轉換為 unsigned native int,並在溢位時引發 OverflowException。
Conv.Ovf.U1 將位於計算堆疊頂部的有符號值轉換為 unsigned int8 並將其擴充套件為 int32,並在溢位時引發 OverflowException。
Conv.Ovf.U1.Un 將位於計算堆疊頂部的無符號值轉換為 unsigned int8 並將其擴充套件為 int32,並在溢位時引發 OverflowException。
Conv.Ovf.U2 將位於計算堆疊頂部的有符號值轉換為 unsigned int16 並將其擴充套件為 int32,並在溢位時引發 OverflowException。
Conv.Ovf.U2.Un 將位於計算堆疊頂部的無符號值轉換為 unsigned int16 並將其擴充套件為 int32,並在溢位時引發 OverflowException。
Conv.Ovf.U4 將位於計算堆疊頂部的有符號值轉換為 unsigned int32,並在溢位時引發 OverflowException。
Conv.Ovf.U4.Un 將位於計算堆疊頂部的無符號值轉換為 unsigned int32,並在溢位時引發 OverflowException。
Conv.Ovf.U8 將位於計算堆疊頂部的有符號值轉換為 unsigned int64,並在溢位時引發 OverflowException。
Conv.Ovf.U8.Un 將位於計算堆疊頂部的無符號值轉換為 unsigned int64,並在溢位時引發 OverflowException。
Conv.R.Un 將位於計算堆疊頂部的無符號整數值轉換為 float32。
Conv.R4 將位於計算堆疊頂部的值轉換為 float32。
Conv.R8 將位於計算堆疊頂部的值轉換為 float64。
Conv.U 將位於計算堆疊頂部的值轉換為 unsigned native int,然後將其擴充套件為 native int。
Conv.U1 將位於計算堆疊頂部的值轉換為 unsigned int8,然後將其擴充套件為 int32。
Conv.U2 將位於計算堆疊頂部的值轉換為 unsigned int16,然後將其擴充套件為 int32。
Conv.U4 將位於計算堆疊頂部的值轉換為 unsigned int32,然後將其擴充套件為 int32。
Conv.U8 將位於計算堆疊頂部的值轉換為 unsigned int64,然後將其擴充套件為 int64。
Cpblk 將指定數目的位元組從源地址複製到目標地址。
Cpobj 將位於物件(&、* 或 native int 型別)地址的值型別複製到目標物件(&、* 或 native int 型別)的地址。
Div 將兩個值相除並將結果作為浮點(F 型別)或商(int32 型別)推送到計算堆疊上。
Div.Un 兩個無符號整數值相除並將結果 ( int32 ) 推送到計算堆疊上。
Dup 複製計算堆疊上當前最頂端的值,然後將副本推送到計算堆疊上。
Endfilter 將控制從異常的 filter 子句轉移回公共語言結構 (CLI) 異常處理程式。
Endfinally 將控制從異常塊的 fault 或 finally 子句轉移回公共語言結構 (CLI) 異常處理程式。
Initblk 將位於特定地址的記憶體的指定塊初始化為給定大小和初始值。
Initobj 將位於指定地址的值型別的每個欄位初始化為空引用或適當的基元型別的 0。
Isinst 測試物件引用(O 型別)是否為特定類的例項。
Jmp 退出當前方法並跳至指定方法。
Ldarg 將引數(由指定索引值引用)載入到堆疊上。
Ldarg.0 將索引為 0 的引數載入到計算堆疊上。
Ldarg.1 將索引為 1 的引數載入到計算堆疊上。
Ldarg.2 將索引為 2 的引數載入到計算堆疊上。
Ldarg.3 將索引為 3 的引數載入到計算堆疊上。
Ldarg.S 將引數(由指定的短格式索引引用)載入到計算堆疊上。
Ldarga 將引數地址載入到計算堆疊上。
Ldarga.S 以短格式將引數地址載入到計算堆疊上。
Ldc.I4 將所提供的 int32 型別的值作為 int32 推送到計算堆疊上。
Ldc.I4.0 將整數值 0 作為 int32 推送到計算堆疊上。
Ldc.I4.1 將整數值 1 作為 int32 推送到計算堆疊上。
Ldc.I4.2 將整數值 2 作為 int32 推送到計算堆疊上。
Ldc.I4.3 將整數值 3 作為 int32 推送到計算堆疊上。
Ldc.I4.4 將整數值 4 作為 int32 推送到計算堆疊上。
Ldc.I4.5 將整數值 5 作為 int32 推送到計算堆疊上。
Ldc.I4.6 將整數值 6 作為 int32 推送到計算堆疊上。
Ldc.I4.7 將整數值 7 作為 int32 推送到計算堆疊上。
Ldc.I4.8 將整數值 8 作為 int32 推送到計算堆疊上。
Ldc.I4.M1 將整數值 -1 作為 int32 推送到計算堆疊上。
Ldc.I4.S 將提供的 int8 值作為 int32 推送到計算堆疊上(短格式)。
Ldc.I8 將所提供的 int64 型別的值作為 int64 推送到計算堆疊上。
Ldc.R4 將所提供的 float32 型別的值作為 F (float) 型別推送到計算堆疊上。
Ldc.R8 將所提供的 float64 型別的值作為 F (float) 型別推送到計算堆疊上。
Ldelem 按照指令中指定的型別,將指定陣列索引中的元素載入到計算堆疊的頂部。
Ldelem.I 將位於指定陣列索引處的 native int 型別的元素作為 native int 載入到計算堆疊的頂部。
Ldelem.I1 將位於指定陣列索引處的 int8 型別的元素作為 int32 載入到計算堆疊的頂部。
Ldelem.I2 將位於指定陣列索引處的 int16 型別的元素作為 int32 載入到計算堆疊的頂部。
Ldelem.I4 將位於指定陣列索引處的 int32 型別的元素作為 int32 載入到計算堆疊的頂部。
Ldelem.I8 將位於指定陣列索引處的 int64 型別的元素作為 int64 載入到計算堆疊的頂部。
Ldelem.R4 將位於指定陣列索引處的 float32 型別的元素作為 F 型別(浮點型)載入到計算堆疊的頂部。
Ldelem.R8 將位於指定陣列索引處的 float64 型別的元素作為 F 型別(浮點型)載入到計算堆疊的頂部。
Ldelem.Ref 將位於指定陣列索引處的包含物件引用的元素作為 O 型別(物件引用)載入到計算堆疊的頂部。
Ldelem.U1 將位於指定陣列索引處的 unsigned int8 型別的元素作為 int32 載入到計算堆疊的頂部。
Ldelem.U2 將位於指定陣列索引處的 unsigned int16 型別的元素作為 int32 載入到計算堆疊的頂部。
Ldelem.U4 將位於指定陣列索引處的 unsigned int32 型別的元素作為 int32 載入到計算堆疊的頂部。
Ldelema 將位於指定陣列索引的陣列元素的地址作為 & 型別(託管指標)載入到計算堆疊的頂部。
Ldfld 查詢物件中其引用當前位於計算堆疊的欄位的值。
Ldflda 查詢物件中其引用當前位於計算堆疊的欄位的地址。
Ldftn 將指向實現特定方法的本機程式碼的非託管指標(native int 型別)推送到計算堆疊上。
Ldind.I 將 native int 型別的值作為 native int 間接載入到計算堆疊上。
Ldind.I1 將 int8 型別的值作為 int32 間接載入到計算堆疊上。
Ldind.I2 將 int16 型別的值作為 int32 間接載入到計算堆疊上。
Ldind.I4 將 int32 型別的值作為 int32 間接載入到計算堆疊上。
Ldind.I8 將 int64 型別的值作為 int64 間接載入到計算堆疊上。
Ldind.R4 將 float32 型別的值作為 F (float) 型別間接載入到計算堆疊上。
Ldind.R8 將 float64 型別的值作為 F (float) 型別間接載入到計算堆疊上。
Ldind.Ref 將物件引用作為 O(物件引用)型別間接載入到計算堆疊上。
Ldind.U1 將 unsigned int8 型別的值作為 int32 間接載入到計算堆疊上。
Ldind.U2 將 unsigned int16 型別的值作為 int32 間接載入到計算堆疊上。
Ldind.U4 將 unsigned int32 型別的值作為 int32 間接載入到計算堆疊上。
Ldlen 將從零開始的、一維陣列的元素的數目推送到計算堆疊上。
Ldloc 將指定索引處的區域性變數載入到計算堆疊上。
Ldloc.0 將索引 0 處的區域性變數載入到計算堆疊上。
Ldloc.1 將索引 1 處的區域性變數載入到計算堆疊上。
Ldloc.2 將索引 2 處的區域性變數載入到計算堆疊上。
Ldloc.3 將索引 3 處的區域性變數載入到計算堆疊上。
Ldloc.S 將特定索引處的區域性變數載入到計算堆疊上(短格式)。
Ldloca 將位於特定索引處的區域性變數的地址載入到計算堆疊上。
Ldloca.S 將位於特定索引處的區域性變數的地址載入到計算堆疊上(短格式)。
Ldnull 將空引用(O 型別)推送到計算堆疊上。
Ldobj 將地址指向的值型別物件複製到計算堆疊的頂部。
Ldsfld 將靜態欄位的值推送到計算堆疊上。
Ldsflda 將靜態欄位的地址推送到計算堆疊上。
Ldstr 推送對元資料中儲存的字串的新物件引用。
Ldtoken 將元資料標記轉換為其執行時表示形式,並將其推送到計算堆疊上。
Ldvirtftn 將指向實現與指定物件關聯的特定虛方法的本機程式碼的非託管指標(native int 型別)推送到計算堆疊上。
Leave 退出受保護的程式碼區域,無條件將控制轉移到特定目標指令。
Leave.S 退出受保護的程式碼區域,無條件將控制轉移到目標指令(縮寫形式)。
Localloc 從本地動態記憶體池分配特定數目的位元組並將第一個分配的位元組的地址(瞬態指標,* 型別)推送到計算堆疊上。
Mkrefany 將對特定型別例項的型別化引用推送到計算堆疊上。
Mul 將兩個值相乘並將結果推送到計算堆疊上。
Mul.Ovf 將兩個整數值相乘,執行溢位檢查,並將結果推送到計算堆疊上。
Mul.Ovf.Un 將兩個無符號整數值相乘,執行溢位檢查,並將結果推送到計算堆疊上。
Neg 對一個值執行求反並將結果推送到計算堆疊上。
Newarr 將對新的從零開始的一維陣列(其元素屬於特定型別)的物件引用推送到計算堆疊上。
Newobj 建立一個值型別的新物件或新例項,並將物件引用(O 型別)推送到計算堆疊上。
Nop 如果修補操作碼,則填充空間。儘管可能消耗處理週期,但未執行任何有意義的操作。
Not 計算堆疊頂部整數值的按位求補並將結果作為相同的型別推送到計算堆疊上。
Or 計算位於堆疊頂部的兩個整數值的按位求補並將結果推送到計算堆疊上。
Pop 移除當前位於計算堆疊頂部的值。
Prefix1 基礎結構。此指令為保留指令。
Prefix2 基礎結構。此指令為保留指令。
Prefix3 基礎結構。此指令為保留指令。
Prefix4 基礎結構。此指令為保留指令。
Prefix5 基礎結構。此指令為保留指令。
Prefix6 基礎結構。此指令為保留指令。
Prefix7 基礎結構。此指令為保留指令。
Prefixref 基礎結構。此指令為保留指令。
Readonly 指定後面的陣列地址操作在執行時不執行型別檢查,並且返回可變性受限的託管指標。
Refanytype 檢索嵌入在型別化引用內的型別標記。
Refanyval 檢索嵌入在型別化引用內的地址(& 型別)。
Rem 將兩個值相除並將餘數推送到計算堆疊上。
Rem.Un 將兩個無符號值相除並將餘數推送到計算堆疊上。
Ret 從當前方法返回,並將返回值(如果存在)從呼叫方的計算堆疊推送到被呼叫方的計算堆疊上。
Rethrow 再次引發當前異常。
Shl 將整數值左移(用零填充)指定的位數,並將結果推送到計算堆疊上。
Shr 將整數值右移(保留符號)指定的位數,並將結果推送到計算堆疊上。
Shr.Un 將無符號整數值右移(用零填充)指定的位數,並將結果推送到計算堆疊上。
Sizeof 將提供的值型別的大小(以位元組為單位)推送到計算堆疊上。
Starg 將位於計算堆疊頂部的值儲存到位於指定索引的引數槽中。
Starg.S 將位於計算堆疊頂部的值儲存在引數槽中的指定索引處(短格式)。
Stelem 用計算堆疊中的值替換給定索引處的陣列元素,其型別在指令中指定。
Stelem.I 用計算堆疊上的 native int 值替換給定索引處的陣列元素。
Stelem.I1 用計算堆疊上的 int8 值替換給定索引處的陣列元素。
Stelem.I2 用計算堆疊上的 int16 值替換給定索引處的陣列元素。
Stelem.I4 用計算堆疊上的 int32 值替換給定索引處的陣列元素。
Stelem.I8 用計算堆疊上的 int64 值替換給定索引處的陣列元素。
Stelem.R4 用計算堆疊上的 float32 值替換給定索引處的陣列元素。
Stelem.R8 用計算堆疊上的 float64 值替換給定索引處的陣列元素。
Stelem.Ref 用計算堆疊上的物件 ref 值(O 型別)替換給定索引處的陣列元素。
Stfld 用新值替換在物件引用或指標的欄位中儲存的值。
Stind.I 在所提供的地址儲存 native int 型別的值。
Stind.I1 在所提供的地址儲存 int8 型別的值。
Stind.I2 在所提供的地址儲存 int16 型別的值。
Stind.I4 在所提供的地址儲存 int32 型別的值。
Stind.I8 在所提供的地址儲存 int64 型別的值。
Stind.R4 在所提供的地址儲存 float32 型別的值。
Stind.R8 在所提供的地址儲存 float64 型別的值。
Stind.Ref 儲存所提供地址處的物件引用值。
Stloc 從計算堆疊的頂部彈出當前值並將其儲存到指定索引處的區域性變數列表中。
Stloc.0 從計算堆疊的頂部彈出當前值並將其儲存到索引 0 處的區域性變數列表中。
Stloc.1 從計算堆疊的頂部彈出當前值並將其儲存到索引 1 處的區域性變數列表中。
Stloc.2 從計算堆疊的頂部彈出當前值並將其儲存到索引 2 處的區域性變數列表中。
Stloc.3 從計算堆疊的頂部彈出當前值並將其儲存到索引 3 處的區域性變數列表中。
Stloc.S 從計算堆疊的頂部彈出當前值並將其儲存在區域性變數列表中的 index 處(短格式)。
Stobj 將指定型別的值從計算堆疊複製到所提供的記憶體地址中。
Stsfld 用來自計算堆疊的值替換靜態欄位的值。
Sub 從其他值中減去一個值並將結果推送到計算堆疊上。
Sub.Ovf 從另一值中減去一個整數值,執行溢位檢查,並且將結果推送到計算堆疊上。
Sub.Ovf.Un 從另一值中減去一個無符號整數值,執行溢位檢查,並且將結果推送到計算堆疊上。
Switch 實現跳轉表。
Tailcall 執行字尾的方法呼叫指令,以便在執行實際呼叫指令前移除當前方法的堆疊幀。
Throw 引發當前位於計算堆疊上的異常物件。
Unaligned 指示當前位於計算堆疊上的地址可能沒有與緊接的 ldind、stind、ldfld、stfld、ldobj、stobj、initblk 或 cpblk 指令的自然大小對齊。
Unbox 將值型別的已裝箱的表示形式轉換為其未裝箱的形式。
Unbox.Any 將指令中指定型別的已裝箱的表示形式轉換成未裝箱形式。
Volatile 指定當前位於計算堆疊頂部的地址可以是易失的,並且讀取該位置的結果不能被快取,或者對該地址的多個儲存區不能被取消。
Xor 計算位於計算堆疊頂部的兩個值的按位異或,並且將結果推送到計算堆疊上。

相關推薦

通過IL理解C# try catch finally真正return 返回

首先,我們先寫一個簡單的tryfinally 語句,來看一下IL反編譯後的程式碼 ,關於IL的一些知識,可以參考這個人的部落格(http://www.cnblogs.com/zery/p/3366175.html)。或者自己百度一下.NET IL 網上會有很多這

Try Catch Finally Finally的代碼在什麽時候不被執行

增加 finall 部分 內存泄漏 無限循環 cmd com 點擊 關閉 近日執行一段陳舊的代碼,一個Batch執行EXE,每日無限循環。唯一可以停掉該Batch的方法,就是直接將進程殺掉,或者在Batch的CMD窗口關掉X按鈕。 而後,進程中永遠都會增加一個Excel的執

C# try catch finally簡單介紹和應用

val hat CA one ... 出錯 結構 介紹 有關 今天看代碼書的時候,有用到try--catch--finally,然後就查了下具體的註意事項和應用。 簡單來說就是:   try {     //有可能出錯誤的代碼或者代碼片段   }   catch{

try catch finally returne的執行順序

表達 static 之前 turn 返回結果 出現 code return 是個 結論:1、不管有沒有出現異常,finally塊中代碼都會執行;2、當try和catch中有return時,finally仍然會執行;3、finally是在return後面的表達式運算後執行的(

Javatry catch finally 有異常和return時處理先後

public class TestDemo { private static String output = ""; public static void foo(int i) { try { if (i == 1) { throw new Exception(); }

try catch finallyfinally是如何逃過return必死的命運

try catch 和finally這種語句我想大家都已經熟的不能再熟了,今天我們來看一看finally為什麼能夠逃脫掉return還能執行的祕密。 首先我們來看一段程式碼來熱熱身: 檢視上面的程式碼能夠讓我們明顯感覺到finally可能是在return之後執行的,不

try-catch-finally,如果在catchreturn了,finally的程式碼還會執行麼,原理是什麼?(異常相關四)

答案:會執行,在return 前執行 /* * java面試題20--如果catch裡面有return語句,finally裡面的程式碼還會執行嗎? */ public class FinallyDemo2 { public static void main(St

關於javatry-catch-finally語句和return

本部落格所有轉載文章的所有權都歸原作者所有,這裡只是共享以及傳播知識作用,每篇文章都會在開頭標明出處,請尊重原作者版權。 第一:return語句並不是函式的最終出口,如果有finally語句,這在return之後還會執行finally(return的值會暫存在棧裡面,

try..catch..finally執行順序return

new lin name 改變 args catch 而是 變量 see 結論:1、不管有木有出現異常,finally塊中代碼都會執行;2、當try和catch中有return時,finally仍然會執行;3、finally是在return後面的表達式運算後執行的(此時並沒

try catch finally在有return的情況下的執行順序

1、不管有沒有出現異常,finally塊中的程式碼都會被執行; 2、當try和catch中有return時,finally仍然會執行; 3、finally是在return後面的表示式運算後執行的 此時

try catch finally 裡有return的執行機制

1、首先明確不管try 和catch裡面有沒有return ,finally裡的程式碼都會執行 2、如果try和catch裡面有return(不討論,沒有return的情況,很明顯) (1)finally 無 return

字節碼分析finally塊對return返回的影響

存儲 row 經驗 臨時 his 也不會 路徑 操作數 方法的參數 直接進入主題。看如下代碼: public int test(){ int i=0; try { i=1; return i; } catch (Exc

pythonreturn返回

return 之間 默認 ret 一行 定義 理解 -i 概念 return基本概念: 函數的返回值是函數重要的組成部分。函數的根本在於實現程序的部分功能,所以很多時候我們需要將函數執行後的結果返回給程序再由程序做出進一步的操作。可以說是函數的返回值令函數與函數之間,函

C#的異常捕獲機制(try catch finally)

一、C#的異常處理所用到關鍵字 try 用於檢查發生的異常,並幫助傳送任何可能的異常。 catch 以控制權更大的方式處理錯誤,可以有多個catch子句。 finally 無論是否引發了異常,finally的程式碼塊都將被執行。 throw 用於引發異常,可引發預定義異常和自定義異常

Try-Catch-Finally代碼塊return

打印 代碼 style pri bsp 自己 println public row 測試類的原型是這樣子的 public class TryCatchFinallyToReturn { public static void main(String[] args)

Java try--catch-- finally、throw、throws 的用法

一、try {..} catch {..}finally {..}用法 try {   執行的程式碼,其中可能有異常。一旦發現異常,則立即跳到catch執行。否則不會執行catch裡面的內容 } catch (Exception e) {  除非try裡面執行程式碼發生了異常,否則這裡的程式碼不會執行 }

JavaScript處理程式碼可能出現的錯誤資訊 try/catch/finally

用法 try { tryCode - 嘗試執行程式碼塊 } catch(err) { catchCode - 捕獲錯誤的程式碼塊 } finally { finallyCode - 無論 try / catch 結果如何都會執行的程式碼

try-catch-finally語句returnfinally的關係

public class TestTryCatch { public static void main(String[] args) { TestTryCatch test = new TestTryCatch(); int fun = test.fun();

[CareerCup] 14.2 Try-catch-finally Java的異常處理

14.2 In Java, does the finally block get executed if we insert a return statement inside the try block of a try-catch-finally? 這道題問我們Java中的finally塊是否會

Javatry catch finally語句return語句的執行情況總結-程式設計陷阱

前言:有java程式設計基礎的人對java的異常處理機制都會有一定了解,而且可能感覺使用起來也比較簡單,但如果在try catch finally語句塊中遇到return語句,開發者可能就會遇到一些邏輯問題,甚至步入程式設計的陷阱。不信,我們先看看一段小程式,讀