詳解c# Emit技術(shù)
我們常常有一個(gè)應(yīng)用場(chǎng)景,由我們的C#代碼,動(dòng)態(tài)生成一個(gè)EXE,其應(yīng)用場(chǎng)景可以非常多,比如軟件授權(quán),可以輸入授權(quán)信息后,生成一個(gè)授權(quán)的DLL等,那如何實(shí)現(xiàn)這個(gè)功能呢,就要提到一個(gè)技術(shù)Emit。
1、Emit概述
Emit,可以稱為發(fā)出或者產(chǎn)生。在Framework中,與Emit相關(guān)的類基本都存在于System.Reflection.Emit命名
空間下??梢奅mit是作為反射的一個(gè)元素存在的。說道反射,大家應(yīng)該都不陌生,它允許我們查看程序集的元素?fù)?jù),從而取得形如程序集包含哪些類型,類型包
含哪些方法等等大量的信息。但是反射也僅能夠‘看',而Emit則可以在運(yùn)行時(shí)動(dòng)態(tài)生成代碼。接下來就來看看如何用Emit生成代碼。
2、程序集(Assembly)和模塊(Managed Module)
程序集是一個(gè)或多個(gè)模塊、資源文件的邏輯性分組,其次程序集是重用,安全性和版本控制的最小單元。我們所見到的DLL、EXE都可以稱為一個(gè)Assembly,一個(gè)Assembly里面包含多個(gè)Module,不過通常,我們VS編譯的時(shí)候,會(huì)只編譯一個(gè)Module,假如在一個(gè)Assembly中要編譯多個(gè)Module,則要借助csc.exe實(shí)現(xiàn)。
3、動(dòng)態(tài)生成代碼操作
定義程序集
//定義一個(gè)程序集的名稱
var asmName = new AssemblyName("MyClass");
//首先就需要定義一個(gè)程序集
var defAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndSave);
定義模塊,和指定程序集的保存名稱
//定義一個(gè)構(gòu)建類
var defModuleBuilder = defAssembly.DefineDynamicModule("MyModule", "MyAssembly.dll");
定義一個(gè)類 和方法
//定義一個(gè)類
var defClassBuilder =defModuleBuilder.DefineType("MyClass", TypeAttributes.Public);
//定義一個(gè)方法
var methodBldr = defClassBuilder.DefineMethod("MyMethod",
MethodAttributes.Public,
null,//返回類型
null//參數(shù)的類型
);
以上通過創(chuàng)建,已經(jīng)確定了程序集和模塊,也定義了當(dāng)前模塊中的一個(gè)類和方法,但這個(gè)類的MyMethod方法只定義了一個(gè)聲明,并沒有定義實(shí)體操作,以下就需要應(yīng)用到Emit技術(shù)中一個(gè)技術(shù)OpCode。
OpCode 是描述中間語言 (IL) 指令。這個(gè)指令非常多,可以查看微軟官網(wǎng):https://docs.microsoft.com/zh-cn/dotnet/api/system.reflection.emit.opcodes?view=netframework-4.8
通過OpCode我們可以定義方法的內(nèi)容如下:
//獲取IL生成器
var il = defMethodBuilder.GetILGenerator();
//定義一個(gè)字符串
il.Emit(OpCodes.Ldstr, "生成的第一個(gè)程序");
//調(diào)用一個(gè)函數(shù)
il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));
//返回到方法開始(返回)
il.Emit(OpCodes.Ret);
通過以上的定義,我們完成了一個(gè)程序集、模塊、類和方法的定義,我們?cè)趺窗岩陨系亩x的信息進(jìn)行創(chuàng)建和保存,需要調(diào)用以下函數(shù):
//創(chuàng)建類型
defClassBuilder.CreateType();
//保存程序集
defAssembly.Save("MyAssemblydll");
我們可以在運(yùn)行程序看到如下效果:

以下通過創(chuàng)建程序集,并且調(diào)用的代碼如下:
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()
{
//定義一個(gè)程序集的名稱
var asmName = new AssemblyName("MyAssembly");
//首先就需要定義一個(gè)程序集
var defAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndSave);
//定義一個(gè)構(gòu)建類
var defModuleBuilder = defAssembly.DefineDynamicModule("MyModule", "MyAssembly.dll");
//定義一個(gè)類
var defClassBuilder = defModuleBuilder.DefineType("MyClass", TypeAttributes.Public);
//定義一個(gè)方法
var defMethodBuilder = defClassBuilder.DefineMethod("MyMethod",
MethodAttributes.Public,
null,//返回類型
null//參數(shù)類型
);
//獲取IL生成器
var il = defMethodBuilder.GetILGenerator();
//定義一個(gè)字符串
il.Emit(OpCodes.Ldstr, "生成的第一個(gè)程序");
//調(diào)用一個(gè)函數(shù)
il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));
//返回到方法開始(返回)
il.Emit(OpCodes.Ret);
//創(chuàng)建類型
defClassBuilder.CreateType();
//保存程序集
defAssembly.Save("MyAssembly.dll");
}
}
}
顯示效果如下:

以上就是詳解c# Emit技術(shù)的詳細(xì)內(nèi)容,更多關(guān)于c# Emit技術(shù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#實(shí)現(xiàn)文件上傳下載Excel文檔示例代碼
這篇文章主要介紹了C#實(shí)現(xiàn)文件上傳下載Excel文檔示例代碼,需要的朋友可以參考下2017-08-08
C# winform實(shí)現(xiàn)登陸次數(shù)限制
這篇文章主要介紹了C# winform實(shí)現(xiàn)登陸次數(shù)限制,相信大家都遇到過網(wǎng)站在用戶多次輸錯(cuò)密碼之后會(huì)自動(dòng)把賬戶凍結(jié)的情況,這種功能如何實(shí)現(xiàn),下面小編為大家分享實(shí)現(xiàn)方法2016-05-05

