動態編譯的好處其實有很多,但是我發現很多人其實沒有真正理解或者沒有靈活運用動態編譯,使得這 麼強大的一個功能變成了雞肋。在我自己使用的工具庫中有很多地方都使用了動態編譯,以後我會慢慢 把工具庫中的代碼都發布出來,所以先把動態編譯的相關知識點整理了一下
什麼是動態編譯?
我的個人理解就是,在程序運行期間,將C#代碼的字符串編譯成為程序集對象,並通過反射該程 序集調用編譯後的成員。
比較容易理解的一種解釋就是類似於SqlServer中的
Exec
('select * from [table]')
或者Javascript中的
var a = eval("(function()
{return 1;})")
為什麼要使用動態編譯?
1.為了比較方便的解決一些難題
例如,計算 一個公式的字符串的值"2+3*(4-1)/5%7"
要計算這個公式的值,把他編譯後直接輸出無疑是 最簡單有效的方法,就比如這樣
//formula = 2 + 3 * (4 - 1) / 5 % 7
public decimal GetValue(string formula)
{
string code = @"
public class Class1
{
public static decimal GetValue()
{
return (decimal)(" + formula + @");
}
}
";
Type type = 動態編譯(code);
return (decimal)type.GetMethod("GetValue").Invoke(null, null);
}
上面說的這種情況是最基本的一種情況,也是最容易理解的一種情況(就我個人來說是不推薦 的,因為編譯一個程序集本身對資源的消耗是很大了,這種公式編譯後的對象正常情況下是無法卸載的,如 果動態編譯只為了使用一次是極為不明智的)
2.為了程序的性能更好
3,為了程序更靈活
4,為了更好的擴展性
5,.......
ps:2,3,4這些會在下一篇文章中提到,這裡先 賣個關子。
怎麼使用動態編譯
先構造一個方便使用的動態編譯的方法
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">編譯代碼中需要引用的類型</param>
/// <returns></returns>
public static Assembly CompileAssembly(string code, params Type[] usingTypes)
{
CompilerParameters compilerParameters = new CompilerParameters();//動態編譯中使用的參數對象
compilerParameters.GenerateExecutable = false;//不需要生成可執行文件
compilerParameters.GenerateInMemory = true;//直接在內存中運行
//添加需要引用的類型
HashSet<string> ns = new HashSet<string>();//用來保存命名空間,這個對象的4.0的,如果是2.0的框架可以使用Dictionary代替
foreach (var type in usingTypes)
{
ns.Add("using " + type.Namespace + ";" + Environment.NewLine);//記錄命名空間,因為不想重復所以使用了HashSet
compilerParameters.ReferencedAssemblies.Add(type.Module.FullyQualifiedName);//這個相當於引入dll
}
code = string.Concat(ns) + code;//加入using命名空間的代碼,即使原來已經有了也不會報錯的
//聲明編譯器
using (CSharpCodeProvider objCSharpCodePrivoder = new CSharpCodeProvider())
{
//開始編譯
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;
}
}
}
}
}
再來就是調用部分的代碼了,還是剛才那個計算的例子
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 + @");
}
}
";
//第二個參數就是這個類中所有用到的類型,包括隱式類型
Type type = DynamicCompile_1.CompileAssembly(code, typeof(decimal)).GetType("Class1");
return (decimal)type.GetMethod("GetValue").Invoke(null, null);
}
}
}
運行後直接就可以看到效果了,所有代碼都在這裡了就不提供下載了哈
查看本欄目