詳解c# Emit技術(shù)
我們常常有一個應(yīng)用場景,由我們的C#代碼,動態(tài)生成一個EXE,其應(yīng)用場景可以非常多,比如軟件授權(quán),可以輸入授權(quán)信息后,生成一個授權(quán)的DLL等,那如何實現(xiàn)這個功能呢,就要提到一個技術(shù)Emit。
1、Emit概述
Emit,可以稱為發(fā)出或者產(chǎn)生。在Framework中,與Emit相關(guān)的類基本都存在于System.Reflection.Emit命名
空間下。可見Emit是作為反射的一個元素存在的。說道反射,大家應(yīng)該都不陌生,它允許我們查看程序集的元素?fù)?jù),從而取得形如程序集包含哪些類型,類型包
含哪些方法等等大量的信息。但是反射也僅能夠‘看',而Emit則可以在運行時動態(tài)生成代碼。接下來就來看看如何用Emit生成代碼。
2、程序集(Assembly)和模塊(Managed Module)
程序集是一個或多個模塊、資源文件的邏輯性分組,其次程序集是重用,安全性和版本控制的最小單元。我們所見到的DLL、EXE都可以稱為一個Assembly,一個Assembly里面包含多個Module,不過通常,我們VS編譯的時候,會只編譯一個Module,假如在一個Assembly中要編譯多個Module,則要借助csc.exe實現(xiàn)。
3、動態(tài)生成代碼操作
定義程序集
//定義一個程序集的名稱 var asmName = new AssemblyName("MyClass"); //首先就需要定義一個程序集 var defAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndSave);
定義模塊,和指定程序集的保存名稱
//定義一個構(gòu)建類 var defModuleBuilder = defAssembly.DefineDynamicModule("MyModule", "MyAssembly.dll");
定義一個類 和方法
//定義一個類 var defClassBuilder =defModuleBuilder.DefineType("MyClass", TypeAttributes.Public); //定義一個方法 var methodBldr = defClassBuilder.DefineMethod("MyMethod", MethodAttributes.Public, null,//返回類型 null//參數(shù)的類型 );
以上通過創(chuàng)建,已經(jīng)確定了程序集和模塊,也定義了當(dāng)前模塊中的一個類和方法,但這個類的MyMethod方法只定義了一個聲明,并沒有定義實體操作,以下就需要應(yīng)用到Emit技術(shù)中一個技術(shù)OpCode。
OpCode 是描述中間語言 (IL) 指令。這個指令非常多,可以查看微軟官網(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(); //定義一個字符串 il.Emit(OpCodes.Ldstr, "生成的第一個程序"); //調(diào)用一個函數(shù) il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) })); //返回到方法開始(返回) il.Emit(OpCodes.Ret);
通過以上的定義,我們完成了一個程序集、模塊、類和方法的定義,我們怎么把以上的定義的信息進(jìn)行創(chuàng)建和保存,需要調(diào)用以下函數(shù):
//創(chuàng)建類型 defClassBuilder.CreateType(); //保存程序集 defAssembly.Save("MyAssemblydll");
我們可以在運行程序看到如下效果:
以下通過創(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() { //定義一個程序集的名稱 var asmName = new AssemblyName("MyAssembly"); //首先就需要定義一個程序集 var defAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndSave); //定義一個構(gòu)建類 var defModuleBuilder = defAssembly.DefineDynamicModule("MyModule", "MyAssembly.dll"); //定義一個類 var defClassBuilder = defModuleBuilder.DefineType("MyClass", TypeAttributes.Public); //定義一個方法 var defMethodBuilder = defClassBuilder.DefineMethod("MyMethod", MethodAttributes.Public, null,//返回類型 null//參數(shù)類型 ); //獲取IL生成器 var il = defMethodBuilder.GetILGenerator(); //定義一個字符串 il.Emit(OpCodes.Ldstr, "生成的第一個程序"); //調(diào)用一個函數(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ù)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Winform界面中實現(xiàn)菜單列表的動態(tài)個性化配置管理方法
下面小編就為大家分享一篇Winform界面中實現(xiàn)菜單列表的動態(tài)個性化配置管理方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2017-11-11Unity通過腳本創(chuàng)建網(wǎng)格Mesh的方法
Unity中的網(wǎng)格作為組件不能脫離物體單獨存在,通過新建腳本來實現(xiàn)相關(guān)操作,本文重點給大家介紹Unity通過腳本創(chuàng)建網(wǎng)格Mesh的方法,感興趣的朋友一起看看吧2022-04-04c# Form中的鍵盤響應(yīng)具體實現(xiàn)思路
在全屏Form中加上鍵盤ESC的響應(yīng),實現(xiàn)的效果就是:全屏中press鍵盤上的Escape鍵,程序結(jié)束,具體實現(xiàn)步驟如下,感興趣的朋友可以參考下哈2013-06-06c#創(chuàng)建vc可調(diào)用的com組件方法分享
本文詳細(xì)闡述如何用C#創(chuàng)建一個COM組件,并能用VC6.0等調(diào)用,大家參考使用2013-12-12