当前位置: 首页 > news >正文

AScript函数体系详解

AScript是一个开源的C#动态脚本解析执行库,提供了完整的函数支持体系:
  • 脚本内定义函数 → 定义与调用(与C#语法一致)
  • 外部注入函数 → C# 注入方法
  • 获取函数 → 返回脚本内定义的函数或者外部注入的函数
  • 生成Lambda  → 生成 .NET Expression/Lambda
  • 生成委托 → 动态脚本生成可复用委托

一、脚本内定义函数

脚本内定义函数,使得脚本配置更加灵活,不必依赖于硬编码增加函数。

标准定义

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 + 60=68
5 ";
6 var script = new Script();
7 Assert.AreEqual(68, script.Eval(s));

二、注入外部函数

 脚本上下文中注入函数

脚本上下文ScriptContext中提供了多个重载AddFunc/AddAction方法:

image

image

 image

 注入委托

1 var script = new Script();
2 script.Context.AddFunc<int, int, int>("sum", (a, b) => a + b);
3 Assert.AreEqual(8, script.Eval("sum(3,5)"));

注入类静态方法

注入公开静态方法,不会注入泛型方法。

1 var script = new Script();
2 script.Context.AddFunc<Math>();
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接口,可对函数编译进行控制,提高编译执行模式的性能。

内部的操作符就是实现IFunctionEvaluatorIFunctionBuilder来处理的。

示例:定义幂运算符**

 1 /// <summary>
 2 /// 幂运算:2**3=8
 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 }

注入CustomFuncton

CustomFunction是由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, IList<ITreeNode> args, out Type returnType);
12 }

顶级上下文中注入函数

ScriptContext.Root顶级上下文中注入函数,可实现全局注入函数,并且所有脚本语言共享。

1 // 顶级上下文中注入函数
2 ScriptContext.Root.AddFunc<int, 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.AddFunc<int, 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.GetFunc<int, int, int>("sum");
8 Assert.AreEqual(30, func(10,20));

四、生成Lambda

动态脚本除了执行计算结果,还可以生成Lambda,该功能可用于将脚本转化为LINQ。

示例:将动态脚本条件语句转化为LINQ查询条件

1 var script = new Script();
2 var whereCondition = script.Lambda<Person, bool>("p.Name=='tom' || p.Name=='jim'", "p");
3 IQueryable<Person> query = ...;
4 // query.Where(p => p.Name=="tom" || p.Name=="jim")
5 var list = query.Where(whereCondition).ToList();

五、生成委托

将脚本编译生成委托,然后在程序调用执行,也是一个常用场景。

1 var script = new Script();
2 var sum = script.Compile<int, int, int>("a+b", "a", "b");
3 Assert.AreEqual(8, sum(3, 5));

 

怎么样,有没有你想要的功能呢?欢迎一起交流学习!

AScript开源地址:https://gitee.com/rockey627/AScript

http://www.jsqmd.com/news/678670/

相关文章:

  • 新手避坑指南:用PCF85063 RTC芯片搞定项目时间,从BCD码转换到寄存器配置详解
  • 2026年3月口碑好的水处理源头厂家哪家有实力,优选实力品牌 - 品牌推荐师
  • 终极iOS 15-16 iCloud绕过方案:如何彻底解除Apple账户锁?
  • 拆解电赛“交流电子负载”:除了拓扑,我们更该关注TVA1421采样与LM5164电源这些细节
  • 2026养生馆加盟品牌综合维度排行与创业适配指南 - 速递信息
  • 手把手教你改造draw.io:实现“无弹窗”创建与“静默”保存的流畅体验
  • 《深度学习入门》聚焦于自然语言处理领域
  • 2026年退休专列旅游品牌排行:新疆游专列在哪儿报名/旅游攻略/火车专列旅游/火车旅行/熊猫专列什邡号/选择指南 - 优质品牌商家
  • 告别手动造数!用SystemVerilog的$fscanf和$sscanf自动解析测试激励
  • 给Go应用做一次‘全身体检’:手把手教你用trace分析GC、调度与协程阻塞
  • 【2026年版|必收藏】程序员/小白入门大模型指南:转行不踩坑,选对方向少走1年弯路
  • Java 25虚拟线程在Spring Boot 3.4中落地全链路实践(从ThreadLocal兼容到Project Loom监控闭环)
  • 2026养生馆加盟品牌排行:5大头部品牌实力解析 - 速递信息
  • 3大技术架构深度解析:VRM-Addon-for-Blender如何实现跨格式模型转换的高性能解决方案
  • 外接球相关
  • 从车灯到自动驾驶域控制器:一文看懂SBC芯片在汽车里的‘七十二变’
  • 2026年成都云梯车租赁权威机构实测排行盘点:成都混凝土切割静态环保破碎/混凝土切割静态环保破碎价格/选择指南 - 优质品牌商家
  • 立体几何 平行和垂直
  • #2026最新装修全包推荐!北京优质装修企业权威榜单,零增项/透明报价/自有工人/环保材料全覆盖 - 十大品牌榜
  • 如何让你的直播告别云端依赖?LocalVocal为你打造本地AI字幕革命
  • 5分钟掌握ModTheSpire:零侵入式杀戮尖塔模组加载器完全指南
  • 用STM32F407的CMSIS-DSP库做FIR滤波,从Matlab设计到C代码移植的完整避坑指南
  • 兰州无人值守地磅厂家推荐榜:电子地磅称/矿区地磅/矿山汽车衡/移动地磅/粮食收购汽车衡/自动称重地磅/选择指南 - 优质品牌商家
  • 上海中考倒计时!2026届初三家长亲述:我们这样筛选一对一辅导,精准避坑 - 品牌测评鉴赏家
  • 多行业适用电动缸厂家优质推荐 - 速递信息
  • 2026年乐山正规升学机构排行:核心维度客观盘点 - 优质品牌商家
  • Azure Kinect DK到手后别急着写代码,先搞定Win10/Win11驱动和固件更新(保姆级避坑)
  • 【会议征稿通知 | 沈阳工业大学主办 | JPCS出版 | EI 、Scopus稳定检索】第十二届先进制造技术与应用材料国际学术会议(ICAMMT 2026)
  • 保姆级教程:用Python 3.11和Poetry从零部署微软GraphRAG v2.7.0(附Azure OpenAI配置)
  • #2026最新装修半包推荐!北京优质装修半包企业权威榜单 - 十大品牌榜