2.【.Net底層剖析】2.stfld指令-給對象的字段賦值
引言:
這篇我們講解在.net IL中間語言中,經常見到的指令stfld。
該指令經常用在給一個對象的字段賦值。
一、指令用途:
MSDN解釋如下:
Replaces the value stored in the field of an object reference or pointer with a new value.
翻譯過來就是:用一個新值替換對象字段的值
二、命名空間和程序集
命名空間是在 System.Reflection.Emit這個里面
程序集是mscorlib(mscorlib.dll中)
三、指令執行機制
工作原理即堆棧轉換行為如下:
按照先后順序:
1.將一個對象引用或指針壓入堆棧
2.將值被壓入堆棧
3.該值和對象的引用/指針從堆棧中彈出,對象的字段更新為替換的值
四、 實例代碼分析:
C#程序:
namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Test test1=new Test();//new一個Test對象 test1.i = 12;//將Test對象的字段i賦值為12 } /// <summary> /// 測試類 /// </summary> public class Test { public int i = 100; } } }
IL 程序Main方法
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint // Code size 16 (0x10) .maxstack 2 .locals init ([0] class ConsoleApplication1.Program/Test test1) IL_0000: nop IL_0001: newobj instance void ConsoleApplication1.Program/Test::.ctor() IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: ldc.i4.s 12 IL_000a: stfld int32 ConsoleApplication1.Program/Test::i IL_000f: ret } // end of method Program::Main
我們來逐行分析下main方法的IL代碼
.entrypoint //定義函數的入口點
// Code size 16 (0x10)//代碼大小為16
.maxstack 2//棧的大小為2
.locals init ([0] class ConsoleApplication1.Program/Test test1)//定義一個變量為test1,存儲在<本地變量列表>中第一個變量中
IL_0000: nop//空操作
IL_0001: newobj instance void ConsoleApplication1.Program/Test::.ctor()//new 一個Test對象,一個引用指向這個對象,引用存放在棧上,
對象存放在堆上面
IL_0006: stloc.0//將引用彈棧,存放到<本地變量列表>中的第一個變量中
IL_0007: ldloc.0//將<本地變量列表>中第一個變量的值壓入堆棧
IL_0008: ldc.i4.s 12//將int 12壓入堆棧
IL_000a: stfld int32 ConsoleApplication1.Program/Test::i//將堆棧的棧頂的值賦值給堆棧的第二個值,即test.i=12
IL_000f: ret//函數返回
五、內存分析
在指令stfld 執行之前的內存圖
堆棧中存放12,test1的地址,<本地變量列表>第一個變量中存放的是test1的地址,堆中存放的是test1指向的一個對象,其中test1.i=100
在指令stfld 執行之后的內存圖
堆棧中的12,test1的地址彈出,<本地變量列表>第一個變量中存放的是test1的地址不變,堆中存放的是test1.i=12
六、總結
本篇主要講的就是對象的字段如何在內存中是如何賦值的,以及從每一行IL指令分析stfld 的執行過程。從底層分析對象的字段的賦值,可以更加清晰地看到賦值的過程。
下篇我會從.net底層剖析參數的傳遞,有興趣的可以關注我哦!