MSIL入門(二)通過對象看IL

来源:https://www.cnblogs.com/yyfh/archive/2020/07/07/13263078.html
-Advertisement-
Play Games

前言 上一篇文章主要介紹了IL的概念以及基礎的示例代碼,在這一篇文章中我們將通過對象調用看IL。 創建對象與調用方法 class Program { static void Main(string[] args) { var obj = new MyClass(); Console.WriteLin ...


前言

上一篇文章主要介紹了IL的概念以及基礎的示例代碼,在這一篇文章中我們將通過對象調用看IL。

創建對象與調用方法

class Program
{
    static void Main(string[] args)
    {
        var obj = new MyClass();
        Console.WriteLine(obj.Say());
    }
}

class MyClass
{
    private const string Str = "Hello";

    public string Say()
    {
        return Str;
    }
}

實例欄位每次創建類型實例的時候都會進行創建,它們屬於這個類型的實例,而靜態欄位由類型的所有實例共用,並且它會在類型載入時創建。某些靜態欄位(文本欄位和映射欄位)從不分配。載入程式只需要記錄要映射的欄位的位置,併在欄位定址時定址這些位置。高級別的編譯器(IL assembler不執行此操作,將其會留給我們開發人員)把實例欄位和三種靜態欄位中的兩種靜態欄位(非文本靜態欄位)的類型,很容易從欄位標記中識別出引用欄位的類型。
當在IL中找到標記時,JIT編譯器不必深入元數據檢索並檢查欄位的標誌。此時所有IL有兩組用於欄位載入和存儲指令。實例欄位的指令為ldfld,ldflda和stfld;靜態欄位的指令是ldsfld,ldsflda和stsfld。嘗試將靜態欄位指令與stance欄位一起使用會導致JIT編譯失敗。逆向組合是可行的,但是它需要將實例指針載入到堆棧上,這當然對於靜態欄位是完全多餘的。對靜態欄位使用實例欄位指令的好處是,它允許以相同的方式訪問靜態欄位和實例欄位。

Callvirt 對象調用後期綁定方法,並且將返回值推送到計算堆棧上。

.field private static literal string Str = "Hello" //靜態公共欄位的定義 

.method private hidebysig static void
    Main(
      string[] args
    ) cil managed
  {
    .entrypoint //主函數,程式的入口
    .maxstack 1 //棧的最大深度
    .locals init (
      [0] class ConsoleApp1.MyClass obj //本地變數的定義
    )

    // [8 9 - 8 10]
    IL_0000: nop //什麼都不做

    // [9 13 - 9 37]
    IL_0001: newobj       instance void ConsoleApp1.MyClass::.ctor() //新建類型為MyClass的對象,並將對象放到堆棧
    IL_0006: stloc.0 //把計算堆棧頂部的值(obj)放到調用堆棧索引0處

    // [10 13 - 10 42]
    IL_0007: ldloc.0      //把調用堆棧索引為0處的值複製到計算堆棧
    IL_0008: callvirt     instance string ConsoleApp1.MyClass::Say() //調用MyClass.Say方法
    IL_000d: call         void  [System.Console]System.Console::WriteLine(string) //調用WriteLine
    IL_0012: nop //什麼都不做

    // [11 9 - 11 10]
    IL_0013: ret //return 

  } // end of method Program::Main

  .method public hidebysig specialname rtspecialname instance void
    .ctor() cil managed
  {
    .maxstack 8 //棧的最大深度

    IL_0000: ldarg.0 //將索引為 0 的參數(this)載入到計算堆棧上。
    IL_0001: call         instance void [System.Runtime]System.Object::.ctor() //從堆棧中取出對象,並調用基類(Object)的構造函數
    IL_0006: nop //什麼都不做
    IL_0007: ret //return 

  } // end of method Program::.ctor
class Program
{
    static void Main(string[] args)
    {
        MyClass obj = new MyClass();
        obj.Name = "Hello";
        obj.Say();
    }
}

public class MyClass
{
    public string Name { get; set; }
        
    public void Say()
    {
        Console.WriteLine(Name);
    }
}
  .class public auto ansi beforefieldinit
  ConsoleApp1.MyClass
    extends [System.Runtime]System.Object
{

  .field private string '<Name>k__BackingField' //自動生成的欄位
    //自動生成獲取屬性值的方法,帶有兩個屬性
    .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor()
      = (01 00 00 00 )
    .custom instance void [System.Diagnostics.Debug]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [System.Diagnostics.Debug]System.Diagnostics.DebuggerBrowsableState)
      = (01 00 00 00 00 00 00 00 ) // ........
      // int32(0) // 0x00000000 

  .method public hidebysig specialname instance string
    get_Name() cil managed
  {
    .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor()
      = (01 00 00 00 )
    .maxstack 8 //棧的最大深度

    // [17 30 - 17 34]
    IL_0000: ldarg.0      //將索引為 0 的參數(this)載入到計算堆棧上。
    IL_0001: ldfld        string ConsoleApp1.MyClass::'<Name>k__BackingField' //從堆棧取出對象,訪問對象的指定欄位,並把欄位值存入堆棧
    IL_0006: ret //return 

  } // end of method MyClass::get_Name

  .method public hidebysig specialname instance void
    set_Name(
      string 'value'
    ) cil managed
  {
    .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor()
      = (01 00 00 00 )
    .maxstack 8 //棧的最大深度

    // [17 35 - 17 39]
    IL_0000: ldarg.0      //將第一個參數(this)載入到計算堆棧上。
    IL_0001: ldarg.1      //將第二個參數載入到計算堆棧上。
    IL_0002: stfld        string ConsoleApp1.MyClass::'<Name>k__BackingField'
    IL_0007: ret //return 

  } // end of method MyClass::set_Name

  .method public hidebysig instance void
    Say() cil managed
  {
    .maxstack 8 //棧的最大深度

    // [20 9 - 20 10]
    IL_0000: nop //什麼都不做

    // [21 13 - 21 37]
    IL_0001: ldarg.0      //將第一個參數(this)載入到計算堆棧上。
    IL_0002: call         instance string ConsoleApp1.MyClass::get_Name()
    IL_0007: call         void [System.Console]System.Console::WriteLine(string)
    IL_000c: nop //什麼都不做

    // [22 9 - 22 10]
    IL_000d: ret

  } // end of method MyClass::Say

  .method public hidebysig specialname rtspecialname instance void
    .ctor() cil managed
  {
    .maxstack 8

    IL_0000: ldarg.0      // this
    IL_0001: call         instance void [System.Runtime]System.Object::.ctor() 
    IL_0006: nop //什麼都不做
    IL_0007: ret //return 

  } // end of method MyClass::.ctor

  .property instance string Name()
  {
    .get instance string ConsoleApp1.MyClass::get_Name() //標識getter
    .set instance void ConsoleApp1.MyClass::set_Name(string) //標識setter
  } // end of property MyClass::Name
} // end of class ConsoleApp1.MyClass


.method private hidebysig static void
    Main(
      string[] args
    ) cil managed
  {
    .entrypoint //主函數,程式的入口
    .maxstack 2
    .locals init (
      [0] class ConsoleApp1.MyClass obj
    ) //本地變數定義

    // [8 9 - 8 10] 
    IL_0000: nop //什麼都不做

    // [9 13 - 9 41]
    IL_0001: newobj       instance void ConsoleApp1.MyClass::.ctor()  //新建類型為MyClass的對象,並將對象放到堆棧
    IL_0006: stloc.0      //把計算堆棧頂部的值(obj)放到調用堆棧索引0處 

    // [10 13 - 10 32]
    IL_0007: ldloc.0      //把調用堆棧索引為0處的值複製到計算堆棧
    IL_0008: ldstr        "Hello" //將字元串"Hello"存入到堆棧
    IL_000d: callvirt     instance void ConsoleApp1.MyClass::set_Name(string) //從堆棧取出對象與參數,並調用對象的指定方法
    IL_0012: nop

    // [11 13 - 11 23]
    IL_0013: ldloc.0      //把調用堆棧索引為0處的值複製到計算堆棧
    IL_0014: callvirt     instance void ConsoleApp1.MyClass::Say() //調用MyClass.Say方法
    IL_0019: nop //什麼都不做

    // [12 9 - 12 10]
    IL_001a: ret //return 

  } // end of method Program::Main


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 關註公眾號【程式員書單】後回覆“book”即可領取30+精品免費電子書 ​ Go 是一種簡單、小巧、令人愉悅的語言。它也有一些犄角旮旯,但絕大部分是經過精心設計的。它的學習速度令人難以置信,並且規避了其他語言中一些不那麼廣為人知的特性。 現如今越來越多的互聯網公司開始使用go語言,有的初創公司開始使 ...
  • 前言 本文的文字及圖片來源於網路,僅供學習、交流使用,不具有任何商業用途,版權歸原作者所有,如有問題請及時聯繫我們以作處理。 作者:打磨蝦 “遲到”了一個月的高考終於要來了。 正好我得到了一份山東新高考模擬考的成績和山東考試院公佈的一分一段表,以及過去三年的普通高考本科普通批首次志願錄取情況統計。2 ...
  • 一. 安裝依賴包 yum install -y wget yum install -y gcc-c++ yum install -y zlib-devel perl-ExtUtils-MakeMaker yum -y install curl-devel expat-devel gettext-de ...
  • 解決辦法: https://tunatore.wordpress.com/2015/06/15/org-jboss-tools-vpe-xulrunner-xulrunnerbundlenotfoundexception-bundle-org-mozilla-xulrunner-win32-win3 ...
  • **前言** Topological sort 又稱 Topological order,這個名字有點迷惑性,因為拓撲排序並不是一個純粹的排序演算法,它只是針對某一類圖,找到一個可以執行的線性順序。 這個演算法聽起來高大上,如今的面試也很愛考,比如當時我在面我司時有整整一輪是基於拓撲排序的設計。 但它其 ...
  • 前言 面試總是會被問到有沒有用過分散式鎖、redis 鎖,大部分讀者平時很少接觸到,所以只能很無奈的回答 “沒有”。本文通過 Spring Boot 整合 redisson 來實現分散式鎖,並結合 demo 測試結果。 首先看下大佬總結的圖 正文 添加依賴 <!--redis--> <depende ...
  • 本文屬於OData系列 目錄 武裝你的WEBAPI-OData入門 武裝你的WEBAPI-OData便捷查詢 武裝你的WEBAPI-OData分頁查詢 武裝你的WEBAPI-OData資源更新Delta 武裝你的WEBAPI-OData之EDM 武裝你的WEBAPI-OData常見問題 武裝你的WE ...
  • abp版本5.9 概述 數據遷移無非就是兩件事情,1、創建資料庫,並根據實體創建對應的表;2、添加一些初始數據 abp的數據遷移也是完成這兩件事,比較特殊的是它是多租戶saas系統,而且支持不同的租戶有獨立的資料庫。所以abp中的遷移要先遷移戶主Host,再遷移租戶Tenant的資料庫 它的遷移定義 ...
一周排行
    -Advertisement-
    Play Games
  • 1. 說明 /* Performs operations on System.String instances that contain file or directory path information. These operations are performed in a cross-pla ...
  • 視頻地址:【WebApi+Vue3從0到1搭建《許可權管理系統》系列視頻:搭建JWT系統鑒權-嗶哩嗶哩】 https://b23.tv/R6cOcDO qq群:801913255 一、在appsettings.json中設置鑒權屬性 /*jwt鑒權*/ "JwtSetting": { "Issuer" ...
  • 引言 集成測試可在包含應用支持基礎結構(如資料庫、文件系統和網路)的級別上確保應用組件功能正常。 ASP.NET Core 通過將單元測試框架與測試 Web 主機和記憶體中測試伺服器結合使用來支持集成測試。 簡介 集成測試與單元測試相比,能夠在更廣泛的級別上評估應用的組件,確認多個組件一起工作以生成預 ...
  • 在.NET Emit編程中,我們探討了運算操作指令的重要性和應用。這些指令包括各種數學運算、位操作和比較操作,能夠在動態生成的代碼中實現對數據的處理和操作。通過這些指令,開發人員可以靈活地進行算術運算、邏輯運算和比較操作,從而實現各種複雜的演算法和邏輯......本篇之後,將進入第七部分:實戰項目 ...
  • 前言 多表頭表格是一個常見的業務需求,然而WPF中卻沒有預設實現這個功能,得益於WPF強大的控制項模板設計,我們可以通過修改控制項模板的方式自己實現它。 一、需求分析 下圖為一個典型的統計表格,統計1-12月的數據。 此時我們有一個需求,需要將月份按季度劃分,以便能夠直觀地看到季度統計數據,以下為該需求 ...
  • 如何將 ASP.NET Core MVC 項目的視圖分離到另一個項目 在當下這個年代 SPA 已是主流,人們早已忘記了 MVC 以及 Razor 的故事。但是在某些場景下 SSR 還是有意想不到效果。比如某些靜態頁面,比如追求首屏載入速度的時候。最近在項目中回歸傳統效果還是不錯。 有的時候我們希望將 ...
  • System.AggregateException: 發生一個或多個錯誤。 > Microsoft.WebTools.Shared.Exceptions.WebToolsException: 生成失敗。檢查輸出視窗瞭解更多詳細信息。 內部異常堆棧跟蹤的結尾 > (內部異常 #0) Microsoft ...
  • 引言 在上一章節我們實戰了在Asp.Net Core中的項目實戰,這一章節講解一下如何測試Asp.Net Core的中間件。 TestServer 還記得我們在集成測試中提供的TestServer嗎? TestServer 是由 Microsoft.AspNetCore.TestHost 包提供的。 ...
  • 在發現結果為真的WHEN子句時,CASE表達式的真假值判斷會終止,剩餘的WHEN子句會被忽略: CASE WHEN col_1 IN ('a', 'b') THEN '第一' WHEN col_1 IN ('a') THEN '第二' ELSE '其他' END 註意: 統一各分支返回的數據類型. ...
  • 在C#編程世界中,語法的精妙之處往往體現在那些看似微小卻極具影響力的符號與結構之中。其中,“_ =” 這一組合突然出現還真不知道什麼意思。本文將深入剖析“_ =” 的含義、工作原理及其在實際編程中的廣泛應用,揭示其作為C#語法奇兵的重要角色。 一、下劃線 _:神秘的棄元符號 下劃線 _ 在C#中並非 ...