在開發強調"彈性"的系統功能時,我偶爾會遇到要由動態產生的複雜運算式決定流程的情境,例如: "( (a + b) > 0 || c > 100 && ( d || e ) )",當運算式參數可預期或是會固定依某些條件變化時,這倒還不算什麼難題;但若是運算式被要求開放使用者或使用元件的開發人員自由設定,就真的不是一般Coding做法能解決的,得寫個Parser解析使用者輸入的運算式再算出結果。而Parser得掌握所有的語法規則,涵蓋各種可能出現的組合,要寫得夠強韌不易出錯實非易事。
相形之下,Javascript裡有eval()函數可用就省事很多,等同於Script Engine本身就提供強大的Parser,可以解析任意Javascript指令並正確執行。
那... 何不直接偷來用?
/*
using System.CodeDom.Compiler;
using System.Reflection;
*/
public class JSEvaluator
{ static Type _jseType = null;
static object _jse = null;
static JSEvaluator()
{ CodeDomProvider provider = CodeDomProvider.CreateProvider("JScript"); CompilerParameters p = new CompilerParameters();
p.GenerateInMemory = true;
CompilerResults r = provider.CompileAssemblyFromSource(p,
@"class JSEvaluator { public function Eval(expr : String) : String { return eval(expr); } }");
Assembly asm = r.CompiledAssembly;
_jseType = asm.GetType("JSEvaluator"); _jse = Activator.CreateInstance(_jseType);
}
public static string Eval(string expr) { return _jseType.InvokeMember("Eval", BindingFlags.InvokeMethod, null, _jse, new object[] { expr }).ToString(); }
}
這樣子,寫一行JSEvaluator.Eval("( ( 4 + 5 * 18 ) > 12 || 'abc' == 'ABC' && 'aaaa'.indexOf('a') > -1 )")就能得到true/false,再複雜(謎之聲: 這範例分明是在找碴吧?)的Javascipt運算式都可以支援! 如此,系統就可以透過程式動態組裝複雜的條件,或是開放具有Javascript背景的開發人員依特定規則自訂條件,條件邏輯可以變化的空間就相當驚人了。
The code passed to the eval method is executed in a restricted security context, unless the string "unsafe" is passed as the second parameter. The restricted security context forbids access to system resources, such as the file system, the network, or the user interface. A security exception is generated if the code attempts to access those resources.