動(dòng)態(tài)編譯的好處其實(shí)有很多,但是我發(fā)現很多人其實(shí)沒(méi)有真正理解或者沒(méi)有靈活運用動(dòng)態(tài)編譯,使得這么強大的一個(gè)功能變成了雞肋。在我自己使用的工具庫中有很多地方都使用了動(dòng)態(tài)編譯,以后我會(huì )慢慢把工具庫中的代碼都發(fā)布出來(lái),所以先把動(dòng)態(tài)編譯的相關(guān)知識點(diǎn)整理了一下
我的個(gè)人理解就是,在程序運行期間,將C#代碼的字符串編譯成為程序集對象,并通過(guò)反射該程序集調用編譯后的成員。
比較容易理解的一種解釋就是類(lèi)似于SqlServer中的
Exec('select * from [table]')
或者Javascript中的
var a = eval("(function(){return 1;})") 1.為了比較方便的解決一些難題
例如,計算一個(gè)公式的字符串的值"2+3*(4-1)/5%7"
要計算這個(gè)公式的值,把他編譯后直接輸出無(wú)疑是最簡(jiǎn)單有效的方法,就比如這樣
//formula = 2 + 3 * (4 - 1) / 5 % 7public decimal GetValue(string formula){ string code = @" public class Class1 { public static decimal GetValue() { return (decimal)(" + formula + @"); } } "; Type type = 動(dòng)態(tài)編譯(code); return (decimal)type.GetMethod("GetValue").Invoke(null, null);}
上面說(shuō)的這種情況是最基本的一種情況,也是最容易理解的一種情況(就我個(gè)人來(lái)說(shuō)是不推薦的,因為編譯一個(gè)程序集本身對資源的消耗是很大了,這種公式編譯后的對象正常情況下是無(wú)法卸載的,如果動(dòng)態(tài)編譯只為了使用一次是極為不明智的)
2.為了程序的性能更好
3,為了程序更靈活
4,為了更好的擴展性
5,.......
ps:2,3,4這些會(huì )在下一篇文章中提到,這里先賣(mài)個(gè)關(guān)子。
先構造一個(gè)方便使用的動(dòng)態(tài)編譯的方法
using Microsoft.CSharp;using System;using System.CodeDom.Compiler;using System.Collections.Generic;using System.Reflection;using System.Text;namespace blqw{ public class DynamicCompile_1 { /// <summary> /// /// </summary> /// <param name="code">需要編譯的C#代碼</param> /// <param name="usingTypes">編譯代碼中需要引用的類(lèi)型</param> /// <returns></returns> public static Assembly CompileAssembly(string code, params Type[] usingTypes) { CompilerParameters compilerParameters = new CompilerParameters();//動(dòng)態(tài)編譯中使用的參數對象 compilerParameters.GenerateExecutable = false;//不需要生成可執行文件 compilerParameters.GenerateInMemory = true;//直接在內存中運行 //添加需要引用的類(lèi)型 HashSet<string> ns = new HashSet<string>();//用來(lái)保存命名空間,這個(gè)對象的4.0的,如果是2.0的框架可以使用Dictionary代替 foreach (var type in usingTypes) { ns.Add("using " + type.Namespace + ";" + Environment.NewLine);//記錄命名空間,因為不想重復所以使用了HashSet compilerParameters.ReferencedAssemblies.Add(type.Module.FullyQualifiedName);//這個(gè)相當于引入dll } code = string.Concat(ns) + code;//加入using命名空間的代碼,即使原來(lái)已經(jīng)有了也不會(huì )報錯的 //聲明編譯器 using (CSharpCodeProvider objCSharpCodePrivoder = new CSharpCodeProvider()) { //開(kāi)始編譯 CompilerResults cr = objCSharpCodePrivoder.CompileAssemblyFromSource(compilerParameters, code); if (cr.Errors.HasErrors)//如果有錯誤 { StringBuilder sb = new StringBuilder(); sb.AppendLine("編譯錯誤:"); foreach (CompilerError err in cr.Errors) { sb.AppendLine(err.ErrorText); } throw new Exception(sb.ToString()); } else { //返回已編譯程序集 return cr.CompiledAssembly; } } } }}
再來(lái)就是調用部分的代碼了,還是剛才那個(gè)計算的例子
using System;namespace blqw.DynamicCompile_Demo{ public class Program { static void Main(string[] args) { decimal val = Calculate("2 + 3 * (4 - 1) / 5 % 7"); if (val == (decimal)(2 + 3 * (4 - 1) / 5 % 7)) { Console.WriteLine(val); } else { Console.WriteLine("錯誤"); } } public static decimal Calculate(string formula) { string code = @" public class Class1 { public static decimal GetValue() { return (decimal)(" + formula + @"); } } "; //第二個(gè)參數就是這個(gè)類(lèi)中所有用到的類(lèi)型,包括隱式類(lèi)型 Type type = DynamicCompile_1.CompileAssembly(code, typeof(decimal)).GetType("Class1"); return (decimal)type.GetMethod("GetValue").Invoke(null, null); } }}
運行后直接就可以看到效果了,所有代碼都在這里了就不提供下載了哈
期待下一篇吧
聯(lián)系客服