首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

Emit 核心概念與完整流程:從零構建動態斐波那契類

     編輯:關於.NET

Reflection Emit (反射發射) 是 .NET 運行時動態生成 IL 代碼、創建程序集與類型的強大機制。本文系統梳理 Emit 核心概念、標準六步流程,並以遞歸斐波那契數列為例,完整演示從 AppDomain 到 CreateType 的實戰過程。

什麼是 Reflection Emit?

屬於 System.Reflection 命名空間的高級功能。普通反射只能「檢測/調用」現有類型;Emit 允許運行時構建全新程序集、模塊、類型、方法,並生成 IL 指令流。典型應用:

  • 動態代理 / AOP 攔截器
  • 高性能序列化 / 映射 (避免反射開銷)
  • ORM 實體材化、SQL 生成
  • 編譯器後端 / DSL 運行時
  • 規則引擎、表達式求值器

標準六步流程

  1. 創建程序集:AppDomain.DefineDynamicAssembly,指定 AssemblyBuilderAccess.RunAndSave。
  2. 定義模塊:AssemblyBuilder.DefineDynamicModule,可指定輸出文件名。
  3. 定義類型:ModuleBuilder.DefineType,設置命名空間、類名、屬性 (TypeAttributes)。
  4. 定義成員:字段、屬性、方法、構造函數、事件等。
  5. 發射 IL 代碼:MethodBuilder.GetILGenerator() → ILGenerator.Emit。
  6. 創建類型:TypeBuilder.CreateType(),可選 AssemblyBuilder.Save 持久化。

工具鏈:Reflector/ILSpy 是寫 IL 的「語法檢查器」

先寫 C# 目標代碼 → 編譯 → 反編譯查看標準 IL → 模仿發射。這比查閱 OpCodes 文檔高效得多。

實戰:動態斐波那契類

目標 C# 代碼

public class Fibonacci
{
    public int Calc(int num)
    {
        if (num == 1 || num == 2) return 1;
        return Calc(num - 1) + Calc(num - 2);
    }
}

簡化後的 IL 邏輯

Calc(num):
    if num == 1: return 1
    if num == 2: return 1
    return Calc(num-1) + Calc(num-2)

關鍵 Emit 代碼片段

var method = typeBuilder.DefineMethod("Calc",
    MethodAttributes.Public, typeof(int), new[] { typeof(int) });

var il = method.GetILGenerator();
var lblReturn1 = il.DefineLabel();
var lblReturnResult = il.DefineLabel();

// if (num == 1) return 1
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Beq_S, lblReturn1);

// if (num == 2) return 1
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldc_I4_2);
il.Emit(OpCodes.Beq_S, lblReturn1);

// Calc(num-1)
il.Emit(OpCodes.Ldarg_0);     // this
il.Emit(OpCodes.Ldarg_1);     // num
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Sub);
il.Emit(OpCodes.Call, method); // 遞歸調用自身

// Calc(num-2)
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldc_I4_2);
il.Emit(OpCodes.Sub);
il.Emit(OpCodes.Call, method);

// add & return
il.Emit(OpCodes.Add);
il.Emit(OpCodes.Br_S, lblReturnResult);

// label: return 1
il.MarkLabel(lblReturn1);
il.Emit(OpCodes.Ldc_I4_1);

// label: return result
il.MarkLabel(lblReturnResult);
il.Emit(OpCodes.Ret);

創建與測試

var type = typeBuilder.CreateType();
asmBuilder.Save("Fibonacci.dll");

var instance = Activator.CreateInstance(type);
var calcMethod = type.GetMethod("Calc");
for (int i = 1; i < 10; i++)
    Console.WriteLine(calcMethod.Invoke(instance, new object[] { i }));

性能提示:用 Emit 替代反射調用

上例中 Activator.CreateInstance + MethodInfo.Invoke 仍有反射開銷。進階做法:再發一個靜態工廠方法返回強類型委托,或直接發射 DynamicMethod 綁定 Func<int, int>,實現零反射調用。

學習路線圖

  1. 基礎:DynamicMethod、OpCodes、棧操作、局部變量、跳轉標簽。
  2. 進階:類型構建、屬性/事件/泛型、異常塊 (BeginExceptionBlock)、調用約定。
  3. 實戰:動態代理、表達式編譯器、高性能序列化器。

Emit 學習曲線陡峭,但掌握後能解決反射性能瓶頸、實現框架級基礎設施,是 .NET 高階開發必備技能。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved