1. 程式人生 > 程式設計 >詳解c# Emit技術

詳解c# Emit技術

我們常常有一個應用場景,由我們的C#程式碼,動態生成一個EXE,其應用場景可以非常多,比如軟體授權,可以輸入授權資訊後,生成一個授權的DLL等,那如何實現這個功能呢,就要提到一個技術Emit。

1、Emit概述

Emit,可以稱為發出或者產生。在Framework中,與Emit相關的類基本都存在於System.Reflection.Emit命名
空間下。可見Emit是作為反射的一個元素存在的。說道反射,大家應該都不陌生,它允許我們檢視程式集的元素據,從而取得形如程式集包含哪些型別,型別包
含哪些方法等等大量的資訊。但是反射也僅能夠‘看',而Emit則可以在執行時動態生成程式碼。接下來就來看看如何用Emit生成程式碼。

2、程式集(Assembly)和模組(Managed Module)

程式集是一個或多個模組、資原始檔的邏輯性分組,其次程式集是重用,安全性和版本控制的最小單元。我們所見到的DLL、EXE都可以稱為一個Assembly,一個Assembly裡面包含多個Module,不過通常,我們VS編譯的時候,會只編譯一個Module,假如在一個Assembly中要編譯多個Module,則要藉助csc.exe實現。

3、動態生成程式碼操作

定義程式集

 //定義一個程式集的名稱
 var asmName = new AssemblyName("MyClass");

//首先就需要定義一個程式集
 var defAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName,AssemblyBuilderAccess.RunAndSave);

定義模組,和指定程式集的儲存名稱

//定義一個構建類
var defModuleBuilder = defAssembly.DefineDynamicModule("MyModule","MyAssembly.dll");

定義一個類 和方法

//定義一個類
 var defClassBuilder =defModuleBuilder.DefineType("MyClass",TypeAttributes.Public);
 //定義一個方法
var methodBldr = defClassBuilder.DefineMethod("MyMethod",MethodAttributes.Public,null,//返回型別
   null//引數的型別
 );

以上通過建立,已經確定了程式集和模組,也定義了當前模組中的一個類和方法,但這個類的MyMethod方法只定義了一個宣告,並沒有定義實體操作,以下就需要應用到Emit技術中一個技術OpCode。

OpCode 是描述中間語言 (IL) 指令。這個指令非常多,可以檢視微軟官網:https://docs.microsoft.com/zh-cn/dotnet/api/system.reflection.emit.opcodes?view=netframework-4.8

通過OpCode我們可以定義方法的內容如下:

//獲取IL生成器
      var il = defMethodBuilder.GetILGenerator();
      //定義一個字串
      il.Emit(OpCodes.Ldstr,"生成的第一個程式");
      //呼叫一個函式
      il.Emit(OpCodes.Call,typeof(Console).GetMethod("WriteLine",new Type[] { typeof(string) }));
      //返回到方法開始(返回)
      il.Emit(OpCodes.Ret);

通過以上的定義,我們完成了一個程式集、模組、類和方法的定義,我們怎麼把以上的定義的資訊進行建立和儲存,需要呼叫以下函式:

    //建立型別
      defClassBuilder.CreateType();

      //儲存程式集
      defAssembly.Save("MyAssemblydll");

我們可以在執行程式看到如下效果:

詳解c# Emit技術

以下通過建立程式集,並且呼叫的程式碼如下:

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

namespace ConsoleApp1
{
  class Program
  {
    static void Main(string[] args)
    {

      CreateAssembly();
      LoadAssembly();

      Console.ReadKey();
    }

    public static void LoadAssembly()
    {
      var ass = AppDomain.CurrentDomain.Load("MyAssembly");
      var m = ass.GetModule("MyModule");
      var ts = m.GetTypes();
      var t = ts.FirstOrDefault();
      if (t != null)
      {
        object obj = Activator.CreateInstance(t);
        var me = t.GetMethod("MyMethod");
        me.Invoke(obj,null);
      }
    }
    public static void CreateAssembly()
    {


      //定義一個程式集的名稱
      var asmName = new AssemblyName("MyAssembly");

      //首先就需要定義一個程式集
      var defAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName,AssemblyBuilderAccess.RunAndSave);
      //定義一個構建類
      var defModuleBuilder = defAssembly.DefineDynamicModule("MyModule","MyAssembly.dll");


      //定義一個類
      var defClassBuilder = defModuleBuilder.DefineType("MyClass",TypeAttributes.Public);

      //定義一個方法
      var defMethodBuilder = defClassBuilder.DefineMethod("MyMethod",//返回型別
        null//引數型別
        );
      //獲取IL生成器
      var il = defMethodBuilder.GetILGenerator();
      //定義一個字串
      il.Emit(OpCodes.Ldstr,new Type[] { typeof(string) }));
      //返回到方法開始(返回)
      il.Emit(OpCodes.Ret);

      //建立型別
      defClassBuilder.CreateType();

      //儲存程式集
      defAssembly.Save("MyAssembly.dll");
    }

  }
}

顯示效果如下:

詳解c# Emit技術

以上就是詳解c# Emit技術的詳細內容,更多關於c# Emit技術的資料請關注我們其它相關文章!