
一、脚本内定义函数脚本内定义函数使得脚本配置更加灵活不必依赖于硬编码增加函数。标准定义1 string s 2 int sum(int a, int b) { 3 return a b; 4 } 5 sum(3, 5); // 8 6 ; 7 var script new Script(); 8 Assert.AreEqual(8, script.Eval(s));简化定义表达式体1 string s 2 int sum(int a, int b) a b; 3 sum(3, 5); // 8 4 ; 5 var script new Script(); 6 Assert.AreEqual(8, script.Eval(s));匿名函数函数名为“_”下划线。1 string s 2 var sum int _(int a, int b) a b; 3 sum.Invoke(3, 5); // 8 4 ; 5 var script new Script(); 6 Assert.AreEqual(8, script.Eval(s));递归函数1 string s 2 int fib(int n) { 3 if (n 1) return n; 4 return fib(n - 1) fib(n - 2); 5 } 6 fib(10); // 55 7 ; 8 var script new Script(); 9 Assert.AreEqual(55, script.Eval(s));函数重载函数名相同参数个数或参数类型不同。1 string s 2 int sum(int a, int b) a b; 3 int sum(int a, int b, int c) a b c; 4 sum(3, 5) sum(10, 20, 30); // 8 6068 5 ; 6 var script new Script(); 7 Assert.AreEqual(68, script.Eval(s));二、注入外部函数脚本上下文中注入函数脚本上下文ScriptContext中提供了多个重载AddFunc/AddAction方法注入委托1 var script new Script(); 2 script.Context.AddFuncint, int, int(sum, (a, b) a b); 3 Assert.AreEqual(8, script.Eval(sum(3,5)));注入类静态方法注入公开静态方法不会注入泛型方法。1 var script new Script(); 2 script.Context.AddFuncMath(); 3 Assert.AreEqual(8, script.Eval(Abs(-8))); // Math.Abs(-8)注入类实例方法注入公开实例方法不会注入泛型方法。1 var random new System.Random(); 2 var script new Script(); 3 // 注入所有公开实例方法 4 script.Context.AddFunc(random); 5 // 只注入Next方法忽略其他方法 6 //script.Context.AddFunc(random, method method.Name Next ? method.Name : null); 7 Console.WriteLine(script.Eval(Next(1, 10))); // random.Next(1, 10)注入IFunctionEvaluator实现IFunctionEvaluator可进行更细微的逻辑控制非常适合实现参数数量、参数类型、返回值类型不固定的函数同时如果实现IFunctionBuilder接口可对函数编译进行控制提高编译执行模式的性能。内部的操作符就是实现IFunctionEvaluator和IFunctionBuilder来处理的。示例定义幂运算符**1 /// summary 2 /// 幂运算2**38 3 /// /summary 4 public class PowerOperator : IFunctionEvaluator, IFunctionBuilder 5 { 6 public static readonly PowerOperator Instance new PowerOperator(); 7 8 public void Build(FunctionBuildArgs e) 9 { 10 var left e.Args[0].Build(e.BuildContext, e.ScriptContext, e.Options); 11 var right e.Args[1].Build(e.BuildContext, e.ScriptContext, e.Options); 12 var v1 Expression.Convert(left, typeof(double)); 13 var v2 Expression.Convert(right, typeof(double)); 14 var v Expression.Call(ExpressionUtils.Method_Math_Power, v1, v2); 15 var maxType ScriptUtils.GetMaxType(left.Type, right.Type); 16 e.Result Expression.Convert(v, maxType); 17 } 18 19 public void Eval(FunctionEvalArgs e) 20 { 21 if (e.Args.Count 2) 22 { 23 var v1 e.Args[0].Eval(e.Context, e.Options, e.Control, out _); 24 var v2 e.Args[1].Eval(e.Context, e.Options, e.Control, out _); 25 var r Math.Pow(Convert.ToDouble(v1), Convert.ToDouble(v2)); 26 var maxType ScriptUtils.GetMaxType(v1.GetType(), v2.GetType()); 27 e.SetResult(ScriptUtils.Convert(r, maxType), maxType); 28 } 29 } 30 }注入CustomFunctonCustomFunction是由ITreeNode构造的函数脚本中定义的函数就是解析成CustomFunction加到上下文环境中的。CustomFunction定义如下1 public class CustomFunction 2 { 3 public string Name { get; } 4 public string[] ArgNames { get; } 5 public Type[] ArgTypes { get; } 6 public Type ReturnType { get; } 7 public ITreeNode Body { get; } 8 9 public CustomFunction(string name, Type returnType, string[] argNames, Type[] argTypes, ITreeNode body); 10 11 public object Eval(ScriptContext context, BuildOptions options, EvalControl control, IListITreeNode args, out Type returnType); 12 }顶级上下文中注入函数在ScriptContext.Root顶级上下文中注入函数可实现全局注入函数并且所有脚本语言共享。1 // 顶级上下文中注入函数 2 ScriptContext.Root.AddFuncint, int, int(sum, (a, b) a b); 3 4 // 直接调用sum函数 5 var script new Script(); 6 Assert.AreEqual(8, script.Eval(sum(3,5)));脚本语言中注入函数只在指定脚本语言中有效其他脚本语言无法调用。1 // C#脚本语言中注入函数 2 CSharpLang.Instance.AddFuncint, int, int(sum, (a, b) a b); 3 4 // 默认C#语言可以直接调用sum函数 5 var script new Script(); 6 // 如果指定其他语言则报错sum函数不存在 7 // script.Context.Langs new [] { python3 }; 8 Assert.AreEqual(8, script.Eval(sum(3,5)));三、获取函数只能从ScriptContext上下文中获取函数如果当前上下文中不存在则从上级搜索不会搜索ScriptLang中的函数。1 string s 2 int sum(int a, int b) a b; 3 sum(3, 5); // 8 4 ; 5 var script new Script(); 6 Assert.AreEqual(8, script.Eval(s)); 7 var func script.Context.GetFuncint, int, int(sum); 8 Assert.AreEqual(30, func(10,20));四、生成Lambda动态脚本除了执行计算结果还可以生成Lambda该功能可用于将脚本转化为LINQ。示例将动态脚本条件语句转化为LINQ查询条件1 var script new Script(); 2 var whereCondition script.LambdaPerson, bool(p.Nametom || p.Namejim, p); 3 IQueryablePerson query ...; 4 // query.Where(p p.Nametom || p.Namejim) 5 var list query.Where(whereCondition).ToList();五、生成委托将脚本编译生成委托然后在程序调用执行也是一个常用场景。1 var script new Script(); 2 var sum script.Compileint, int, int(ab, a, b); 3 Assert.AreEqual(8, sum(3, 5));