.NET Emit 入門教程:第六部分:IL 指令:5:詳解 ILGenerator 指令方法:建立例項指令

路过秋天發表於2024-04-07

前言:

上上篇介紹了 IL 指令的分類以及引數載入指令,該載入指令以 Ld 開頭,將引數載入到棧中,以便於後續執行操作命令。

上一篇介紹引數儲存指令,其指令以 St 開頭,將棧中的資料,儲存到指定的變數中,以方便後續使用。

本篇將介紹建立例項指令,其指令以 New 開頭,用於在執行時動態生成並初始化物件。

建立例項指令簡介

在.NET Emit 中,使用 ILGenerator 建立例項是一項重要的操作,它允許我們動態生成物件例項和陣列例項的程式碼。

透過建立例項指令,我們可以在執行時動態生成並初始化物件,為程式提供更大的靈活性和可擴充套件性。

建立例項指令主要包括 Newobj 指令和 Newarr 指令。

Newobj 指令用於建立新的物件例項,而 Newarr 指令則用於建立新的陣列例項。

這些指令的靈活運用可以幫助我們在執行時動態地生成各種型別的例項,滿足不同場景下的需求。

在本篇文章中,我們將深入探討 ILGenerator 中的建立例項指令,詳細解析其用法和示例程式碼。

透過學習本文內容,讀者將能夠掌握如何利用 ILGenerator 建立物件例項和陣列例項,從而更好地理解和應用.NET Emit 技術。

1、建立例項指令:Newobj

對於該指令,其核心在於如何獲取建構函式並作為引數傳遞,下面看一組示例。

共用程式碼,定義實體(包含無參建構函式、有參建構函式、基本變數):

 public class Entity
 {
     public Entity()
     {

     }
     public Entity(int id)
     {
         this.ID = id;
     }
     public int ID;
 }

共用程式碼,生成程式集,以方便後續對照參考:

AssemblyName assName = new AssemblyName("myAssembly") { Version = new Version("1.1.1.2") };
AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(assName, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder mb = ab.DefineDynamicModule("myModule", "b.dll");
TypeBuilder tb = mb.DefineType("MyNameSpace.MyClass", TypeAttributes.Public | TypeAttributes.Class);

//定義靜態方法
MethodBuilder methodBuilder = tb.DefineMethod("NewObj", MethodAttributes.Public | MethodAttributes.Static, typeof(Entity), new Type[] { });
ILGenerator il = methodBuilder.GetILGenerator();

//il 程式碼處......


Type classType = tb.CreateType();

ab.Save("b.dll");

A、無引數例項化:透過 Type 的 GetConstructor 例項方法獲取型別的建構函式。

ILGenerator il = methodBuilder.GetILGenerator();
il.Emit(OpCodes.Newobj, typeof(Entity).GetConstructor(Type.EmptyTypes));
il.Emit(OpCodes.Ret);     // 返回該值

對照生成:

B、使用引數例項化:

 ILGenerator il = methodBuilder.GetILGenerator();
 il.Emit(OpCodes.Ldc_I4, 999);
 il.Emit(OpCodes.Newobj, typeof(Entity).GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(int) }, null));
 il.Emit(OpCodes.Ret);     // 返回該值

對照生成:

小說明:

這裡建構函式的引數傳入,是透過 Ld 系列指令按順序壓入棧中。

2、建立例項指令:Newarr

該指令用於建立陣列物件,該指令需要指定陣列長度。

A、建立陣列:

ILGenerator il = methodBuilder.GetILGenerator();
il.Emit(OpCodes.Ldc_I4, 6);
il.Emit(OpCodes.Newarr, typeof(Entity));
il.Emit(OpCodes.Ret);     // 返回該值

對照生成程式碼:

小說明:

Newarr 接收的引數,是 Type 型別。

Newobj 接收的引數,是 ConstructorInfo 建構函式型別。

B、對陣列賦值:引用型別

ILGenerator il = methodBuilder.GetILGenerator();

//建立陣列
il.Emit(OpCodes.Ldc_I4, 3);
il.Emit(OpCodes.Newarr, typeof(Entity));

il.DeclareLocal(typeof(Entity[]));
il.DeclareLocal(typeof(Entity));
il.Emit(OpCodes.Stloc_0);//儲存陣列

for (int i = 0; i < 3; i++)
{
    il.Emit(OpCodes.Newobj, typeof(Entity).GetConstructor(Type.EmptyTypes));//定義實體類
    il.Emit(OpCodes.Stloc_1);//儲存實體類

    il.Emit(OpCodes.Ldloc_0);//載入陣列
    il.Emit(OpCodes.Ldc_I4, i);//載入索引
    il.Emit(OpCodes.Ldloc_1);//載入Entity

    il.Emit(OpCodes.Stelem_Ref);//引用型別賦值
}


il.Emit(OpCodes.Ldloc_0);//載入陣列
il.Emit(OpCodes.Ret);     // 返回該值

對照生成程式碼:

C、對陣列賦值:值型別

ILGenerator il = methodBuilder.GetILGenerator();

//建立陣列
il.Emit(OpCodes.Ldc_I4, 3);
il.Emit(OpCodes.Newarr, typeof(DateTime));

il.DeclareLocal(typeof(DateTime[]));
il.DeclareLocal(typeof(DateTime));
il.Emit(OpCodes.Stloc_0);

for (int i = 0; i < 3; i++)
{// 呼叫 DateTime.Parse 方法建立 DateTime 例項
    MethodInfo parseMethod = typeof(DateTime).GetMethod("Parse", new Type[] { typeof(string) });
    il.Emit(OpCodes.Ldstr, DateTime.Now.ToString()); // 傳遞當前時間字串
    il.Emit(OpCodes.Call, parseMethod);    // 呼叫 Parse 方法
    il.Emit(OpCodes.Stloc_1);

    il.Emit(OpCodes.Ldloc_0);//載入陣列
    il.Emit(OpCodes.Ldc_I4, i);//載入索引
    il.Emit(OpCodes.Ldloc_1);//載入Entity


    il.Emit(OpCodes.Stelem, typeof(DateTime));//賦值
}

il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ret);     // 返回該值

對照生成程式碼:

D、陣列取值指令:

總結:

在.NET Emit 入門教程的第六部分中,我們深入探討了 ILGenerator 指令方法,特別是關於建立例項指令的詳細解釋。

ILGenerator 是.NET框架中的一個強大工具,用於在執行時生成和執行IL程式碼。

在這篇文章中,我們學習瞭如何使用 ILGenerator 來建立例項,其中主要涉及到了兩種指令方法:newobj 和 newarr。

透過 newobj 指令,我們可以在IL程式碼中呼叫建構函式來建立類的例項,而 newarr 指令則用於建立陣列例項。

透過學習這些內容,讀者可以更深入地理解 ILGenerator 的使用,並在實際專案中應用動態程式碼生成的技術。

下一篇,我們將學習方法呼叫指令的相關內容。

相關文章