C#脚本引擎之AScript与Z.Expressions.Eval对比

一 简介

1.1 AScript

AScript是一个开源的C#动态脚本解析执行引擎,支持自定义语法,支持扩展多种脚本语言(目前已支持C#/Python3/SQL/JavaScript/Lua五种语言),支持解释执行和编译执行两种执行模式,并且支持编译缓存。

目前官方最新版本:1.7.2

开源地址:

相关文章:

1.2 Z.Expressions.Eval

Z.Expression.Eval是一个非常强大的工具,使得开发人员可以动态编译和执行C#代码和表达式。

  • Execute C# Code
  • Compile C# Code
  • LINQ Dynamic

目前官方最新版本:6.3.13

官网地址:https://eval-expression.net

二 功能

2.1 编译缓存

二者皆支持编译缓存功能:

复制代码
1 // AScript默认不启用缓存
2 var script = new Script();
3 var result = script.Eval("5 + 6", -1);
4 
5 // Z.Expressions.Eval默认启用缓存
6 Z.Expressions.EvalManager.DefaultContext.UseCache = true;
7 var result = Z.Expressions.Eval.Execute<int>("5 + 6");

同时AScript支持缓存版本控制和定时清理缓存功能:

复制代码
 1 /// <summary>
 2 /// 计算表达式,返回结果
 3 /// </summary>
 4 /// <param name="expression">脚本</param>
 5 /// <param name="cacheTime">
 6 /// <para>缓存时长</para>
 7 /// <para>为0表示不使用缓存(默认);</para>
 8 /// <para>-1表示永久缓存;</para>
 9 /// <para>大于0表示缓存时长(单位:毫秒)</para>
10 /// </param>
11 /// <param name="cacheKey">
12 /// 缓存key(如果为空则取表达式字符串)
13 /// </param>
14 /// <param name="cacheVersion">
15 /// 缓存版本号,如果版本号有变化,则重新编译并缓存
16 /// </param>
17 /// <returns></returns>
18 public object Eval(string expression, int cacheTime = 0, string cacheKey = null, string cacheVersion = null);
19 public object Eval(Stream expression, int cacheTime = 0, string cacheKey = null, string cacheVersion = null);
20 public object Eval(Func<string> expression, int cacheTime = 0, string cacheKey = null, string cacheVersion = null);
21 public object Eval(Func<Stream> expression, int cacheTime = 0, string cacheKey = null, string cacheVersion = null);
22 public object EvalFile(string path, int cacheTime = 0, string cacheKey = null, string cacheVersion = null);

场景一

脚本保存在数据库或其他远程端,以业务标识为cacheKey,以修改时间戳为cacheVersion:

复制代码
1 string cacheKey = "xxx";
2 string cacheVersion = "当前版本";
3 var script = new Script();
4 var result = script.Eval(()=>获取脚本, cacheTime: -1, cacheKey, cacheVersion);

如果版本未变化,则直接执行缓存中的委托;

如果版本有变化,则重新获取脚本-->编译-->缓存-->执行。

场景二

脚本保存在文件中,可以调用EvalFile方法,默认cacheKey为文件完整路径,cacheVersion可以设置为修改时间:

复制代码
1 string cacheVersion = System.IO.File.GetLastWriteTimeUtc(path).Ticks.ToString();
2 var script = new Script();
3 var result = script.EvalFile(path, cacheTime: -1, cacheVersion: cacheVersion);

如果文件未修改,则直接执行缓存中的委托;

如果文件有修改,则重新读取文件-->编译-->缓存-->执行。

2.2 Compile

将脚本编译生成委托:

复制代码
1 // AScript
2 var script = new Script();
3 Func<int, int> func = script.Compile<int, int>("a + 5", "a");
4 Func<int, int> func = script.Compile<Func<int, int>>("a + 5", new[] { "a" });
5 
6 // Z.Expressions.Eval
7 Func<int, int> func = Z.Expressions.Eval.Compile<Func<int, int>>("a + 5", "a");

2.3 注入函数

  • 添加类中的方法
复制代码
 1 // AScript
 2 var script = new Script();
 3 // 添加静态方法,支持扩展方法
 4 script.Context.AddFunc(typeof(MyClass));
 5 // 添加实例方法
 6 script.Context.AddFunc(new MyClass());
 7 script.Eval("Sum(5, 6)");
 8 script.Eval("Hello('tom')");
 9 
10 // Z.Expressions.Eval:支持静态方法,不支持实例方法
11 Z.Expressions.EvalManager.DefaultContext.RegisterStaticMethod(typeof(MyClass));
12 Z.Expressions.Eval.Execute("Sum(5, 6)");
13 
14 public class MyClass
15 {
16     public static int Sum(int a, int b) => a + b;
17 
18     public string Hello(string name) => $"hello {name}";
19 }
  • 添加委托
复制代码
1 // AScript
2 var script = new Script();
3 script.Context.AddFunc<int, int, int>("sum", (a, b) => a + b);
4 script.Eval("sum(5, 6)");
5 
6 // Z.Expressions.Eval不支持添加委托

2.4 递归函数

AScript支持定义递归函数,Z.Expressions.Eval不支持递归函数。

复制代码
1 string code = @"
2 int fib(int n) {
3     if (n <= 1) return n;
4     return fib(n - 1) + fib(n - 2);
5 }
6 fib(10)
7 ";
8 var script = new Script();
9 var result = script.Eval<int>(code); // 55

相关文章:AScript之编译递归函数

2.5 Queryable/Enumerable扩展方法

复制代码
 1 string code = @"
 2 var list = new List<int>() { 1, 2, 3, 4 };
 3 return list.Where(x => x > 2).ToList();
 4 ";
 5 
 6 // AScript
 7 var script = new Script();
 8 var result = script.Eval<List<int>>(code);
 9 
10 // Z.Expressions.Eval
11 var result = Z.Expressions.Eval.Execute<List<int>>(code);

2.6 LINQ Dynamic

复制代码
1 List<int> list = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
2 // AScript动态LINQ扩展
3 var result = list.WhereScript(x => "x % 2 == 0").ToList();
4 // Z.Expressions.Eval动态LINQ扩展
5 var result = list.WhereDynamic(x => "x % 2 == 0").ToList();

2.7 LINQ查询语法

AScript支持查询语法,Z.Expressions.Eval不支持。

复制代码
1 string code = @"
2 var list = new List<int>() { 1, 2, 3, 4 };
3 var q = from item in list
4     where item > 2
5     select item * 10;
6 return q.ToList();
7 ";
8 var script = new Script();
9 var result = script.Eval<List<int>>(code);

2.8 混合多语言脚本

AScript脚本中使用@lang/@end指定语言:

复制代码
 1 Script.Langs.Set("js", JavaScriptLang.Instance);
 2 Script.Langs.Set("lua", LuaLang.Instance);
 3 
 4 string code = @"
 5 int a = 12;
 6 string s = 'hello';
 7 
 8 // 调用lua语言中的打包/解包方法
 9 @lang lua
10 local data = string.pack('<i4c10', a, s)
11 local a2,s2 = string.unpack('i4c10', data)
12 @end
13 
14 Console.WriteLine(a2 + s2); // 12hello
15 
16 // 调用js语言中的控制台输出
17 @lang js
18 console.log(a2, s2); // 12,hello
19 @end
20 ";
21 var script = new Script();
22 script.Eval(code);

2.9 层级上下文

  • Z.Expressions.Eval中的EvalContext不支持层级,创建EvalContext实例成本高
  • AScript中的ScriptContext支持层级,ScriptContext.Root为全局顶级上下文
复制代码
 1 // 创建业务上下文:context.Parent为ScriptContext.Root
 2 var context = new ScriptContext();
 3 // TODO: context中设置共享业务数据
 4 context.SetVar("order", order);
 5 
 6 // 创建子上下文1:subContext1.Parent为context
 7 var subContext1 = new ScriptContext(context);
 8 // TODO: subContext1中设置子业务数据
 9 subContext1.SetVar("user", user1);
10 // 执行脚本
11 var script1 = new Script(subContext1);
12 script1.Eval(code1);
13 
14 // 创建子上下文2:subContext2.Parent为context
15 var subContext2 = new ScriptContext(context);
16 // TODO: subContext2中设置子业务数据
17 subContext2.SetVar("user", user2);
18 // 执行脚本
19 var script2 = new Script(subContext2);
20 script2.Eval(code2);

三 性能测试

使用Benchmark工具进行性能基准测试,测试环境:

复制代码
BenchmarkDotNet v0.14.0, Windows 11 (10.0.22621.4317/22H2/2022Update/SunValley2)
AMD Ryzen 7 7840H with Radeon 780M Graphics, 1 CPU, 16 logical and 8 physical cores
.NET SDK 9.0.316
  [Host]     : .NET 6.0.36 (6.0.3624.51421), X64 RyuJIT AVX2
  DefaultJob : .NET 6.0.36 (6.0.3624.51421), X64 RyuJIT AVX2

安装nuget包:

复制代码
install-package AScript
install-package Z.Expressions.Eval

3.1 常量表达式

复制代码
 1 private static readonly string s = "100 * (5 + 5) * (6-2)";
 2 private static readonly int r = 100 * (5 + 5) * (6 - 2);
 3 
 4 // 解释执行
 5 [Benchmark]
 6 public void AScript1()
 7 {
 8     var script = new Script();
 9     var result = script.Eval<int>(s);
10     if (result != r) throw new Exception("result error");
11 }
12 
13 // 编译执行
14 [Benchmark]
15 public void AScript2_Compile()
16 {
17     var script = new Script();
18     var result = script.Eval<int>(s, ECompileMode.All);
19     if (result != r) throw new Exception("result error");
20 }
21 
22 [Benchmark]
23 public void ZExpressions2()
24 {
25     Z.Expressions.EvalManager.DefaultContext.UseCache = false;
26     var result = Z.Expressions.Eval.Execute<int>(s);
27     if (result != r) throw new Exception("result error");
28 }
29 
30 // 编译缓存
31 [Benchmark]
32 public void AScript3_Cache()
33 {
34     var script = new Script();
35     var result = script.Eval<int>(s, -1);
36     if (result != r) throw new Exception("result error");
37 }
38 
39 [Benchmark]
40 public void ZExpressions3()
41 {
42     Z.Expressions.EvalManager.DefaultContext.UseCache = true;
43     var result = Z.Expressions.Eval.Execute<int>(s);
44     if (result != r) throw new Exception("result error");
45 }

测试结果:

性能:AScript > Z.Expressions.Eval

3.2 本地变量

复制代码
 1 private static readonly string s = "var a = 100; var b = 5; var c = 6; a * (b + 5) * (c-2)";
 2 private static readonly int r = 100 * (5 + 5) * (6 - 2);
 3 
 4 // 解释执行
 5 [Benchmark]
 6 public void AScript1()
 7 {
 8     var script = new Script();
 9     var result = script.Eval<int>(s);
10     if (result != r) throw new Exception("result error");
11 }
12 
13 // 编译执行
14 [Benchmark]
15 public void AScript2_Compile()
16 {
17     var script = new Script();
18     // Z.Expressions.Eval不回写临时变量到上下文,这里也关闭变量回写功能,保持功能一致
19     script.Options.RewriteFunctions = false;
20     script.Options.RewriteVariables = false;
21     var result = script.Eval<int>(s, ECompileMode.All);
22     if (result != r) throw new Exception("result error");
23 }
24 
25 [Benchmark]
26 public void ZExpressions2()
27 {
28     Z.Expressions.EvalManager.DefaultContext.UseCache = false;
29     var result = Z.Expressions.Eval.Execute<int>(s);
30     if (result != r) throw new Exception("result error");
31 }
32 
33 // 编译缓存
34 [Benchmark]
35 public void AScript3_Cache()
36 {
37     var script = new Script();
38     // Z.Expressions.Eval不回写临时变量到上下文,这里也关闭变量回写功能,保持功能一致
39     script.Options.RewriteFunctions = false;
40     script.Options.RewriteVariables = false;
41     var result = script.Eval<int>(s, -1);
42     if (result != r) throw new Exception("result error");
43 }
44 
45 [Benchmark]
46 public void ZExpressions3()
47 {
48     Z.Expressions.EvalManager.DefaultContext.UseCache = true;
49     var result = Z.Expressions.Eval.Execute<int>(s);
50     if (result != r) throw new Exception("result error");
51 }

测试结果:

性能:AScript > Z.Expressions.Eval

3.3 注入变量

复制代码
 1 private static readonly string s = "a * (b + 5) * (c-2)";
 2 private static readonly int r = 100 * (5 + 5) * (6 - 2);
 3 
 4 // 解释执行
 5 [Benchmark]
 6 public void AScript1()
 7 {
 8     var script = new Script();
 9     script.Context.SetVar("a", 100);
10     script.Context.SetVar("b", 5);
11     script.Context.SetVar("c", 6);
12     var result = script.Eval<int>(s);
13     if (result != r) throw new Exception("result error");
14 }
15 
16 // 编译执行
17 [Benchmark]
18 public void AScript2_Compile()
19 {
20     var script = new Script();
21     // Z.Expressions.Eval不回写临时变量到上下文,这里也关闭变量回写功能,保持功能一致
22     script.Options.RewriteFunctions = false;
23     script.Options.RewriteVariables = false;
24     script.Context.SetVar("a", 100);
25     script.Context.SetVar("b", 5);
26     script.Context.SetVar("c", 6);
27     var result = script.Eval<int>(s, ECompileMode.All);
28     if (result != r) throw new Exception("result error");
29 }
30 
31 [Benchmark]
32 public void ZExpressions2()
33 {
34     Z.Expressions.EvalManager.DefaultContext.UseCache = false;
35     var result = Z.Expressions.Eval.Execute<int>(s, new { a = 100, b = 5, c = 6 });
36     if (result != r) throw new Exception("result error");
37 }
38 
39 // 编译缓存
40 [Benchmark]
41 public void AScript3_Cache()
42 {
43     var script = new Script();
44     // Z.Expressions.Eval不回写临时变量到上下文,这里也关闭变量回写功能,保持功能一致
45     script.Options.RewriteFunctions = false;
46     script.Options.RewriteVariables = false;
47     script.Context.SetVar("a", 100);
48     script.Context.SetVar("b", 5);
49     script.Context.SetVar("c", 6);
50     var result = script.Eval<int>(s, -1);
51     if (result != r) throw new Exception("result error");
52 }
53 
54 [Benchmark]
55 public void ZExpressions3()
56 {
57     Z.Expressions.EvalManager.DefaultContext.UseCache = true;
58     var result = Z.Expressions.Eval.Execute<int>(s, new { a = 100, b = 5, c = 6 });
59     if (result != r) throw new Exception("result error");
60 }

测试结果:

性能:AScript > Z.Expressions.Eval

3.4 注入方法

复制代码
 1 private static readonly string s = "sum(5, 6)";
 2 private static readonly int r = 11;
 3 
 4 // 解释执行
 5 [Benchmark]
 6 public void AScript1()
 7 {
 8     var script = new Script();
 9     //script.Context.AddFunc<int, int, int>("sum", (a, b) => a + b);
10     script.Context.AddFunc(typeof(MyMethod));
11     var result = script.Eval<int>(s);
12     if (result != r) throw new Exception("result error");
13 }
14 
15 // 编译执行
16 [Benchmark]
17 public void AScript2_Compile()
18 {
19     var script = new Script();
20     // Z.Expressions.Eval不回写临时变量到上下文,这里也关闭变量回写功能,保持功能一致
21     script.Options.RewriteFunctions = false;
22     script.Options.RewriteVariables = false;
23     //script.Context.AddFunc<int, int, int>("sum", (a, b) => a + b);
24     script.Context.AddFunc(typeof(MyMethod));
25     var result = script.Eval<int>(s, ECompileMode.All);
26     if (result != r) throw new Exception("result error");
27 }
28 
29 [Benchmark]
30 public void ZExpressions2()
31 {
32     Z.Expressions.EvalManager.DefaultContext.UseCache = false;
33     Z.Expressions.EvalManager.DefaultContext.RegisterStaticMethod(typeof(MyMethod));
34     var result = Z.Expressions.Eval.Execute<int>(s);
35     if (result != r) throw new Exception("result error");
36 }
37 
38 // 编译缓存
39 [Benchmark]
40 public void AScript3_Cache()
41 {
42     var script = new Script();
43     // Z.Expressions.Eval不回写临时变量到上下文,这里也关闭变量回写功能,保持功能一致
44     script.Options.RewriteFunctions = false;
45     script.Options.RewriteVariables = false;
46     //script.Context.AddFunc<int, int, int>("sum", (a, b) => a + b);
47     script.Context.AddFunc(typeof(MyMethod));
48     var result = script.Eval<int>(s, -1);
49     if (result != r) throw new Exception("result error");
50 }
51 
52 [Benchmark]
53 public void ZExpressions3()
54 {
55     Z.Expressions.EvalManager.DefaultContext.UseCache = true;
56     Z.Expressions.EvalManager.DefaultContext.RegisterStaticMethod(typeof(MyMethod));
57     var result = Z.Expressions.Eval.Execute<int>(s);
58     if (result != r) throw new Exception("result error");
59 }
60 
61 private class MyMethod
62 {
63     public static int sum(int a, int b) => a + b;
64 }

测试结果:

性能:AScript > Z.Expressions.Eval

3.5 本地函数

复制代码
 1 private static readonly string s1 = @"
 2 int sum(int a, int b) {
 3     return a + b;
 4 }
 5 sum(5, 6)
 6 ";
 7 
 8 // Z.Expressions.Eval定义函数需要加public修饰
 9 private static readonly string s2 = @"
10 public int sum(int a, int b) {
11     return a + b;
12 }
13 sum(5, 6)
14 ";
15 
16 private static readonly int r = 11;

测试结果:

性能:AScript > Z.Expressions.Eval

3.6 LINQ

复制代码
1 private static readonly List<int> _list = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
2 private static readonly string s = "list.Where(a => a % 2 == 0).ToList().Count";
3 private static readonly int r = 5;

测试结果:

性能:AScript > Z.Expressions.Eval

3.7 编译

仅编译生成委托,不计算结果。

复制代码
 1 private static readonly string s = @"
 2 var list = new List<int>() { 1, 2, 3, 4 };
 3 return list.Where(x => x > 2).ToList();";
 4 
 5 [Benchmark]
 6 public void AScript1()
 7 {
 8     var script = new Script();
 9     var result = script.Compile<List<int>>(s);
10 }
11 
12 [Benchmark]
13 public void ZExpressions1()
14 {
15     Z.Expressions.EvalManager.DefaultContext.UseCache = false;
16     var result = Z.Expressions.Eval.Compile<Func<List<int>>>(s);
17 }
18 
19 [Benchmark]
20 public void AScript2_Cache()
21 {
22     var script = new Script();
23     var result = script.Compile<List<int>>(s, -1);
24 }
25 
26 [Benchmark]
27 public void ZExpressions2()
28 {
29     Z.Expressions.EvalManager.DefaultContext.UseCache = true;
30     var result = Z.Expressions.Eval.Compile<Func<List<int>>>(s);
31 }

测试结果:

性能:二者相差不大

3.8 for循环

复制代码
 1 private static readonly string s = @"
 2 var n = 0;
 3 for (var i = 0; i < 10000; i++) {
 4     n += i;
 5 }
 6 n
 7 ";
 8 private static readonly int r = 9999 * 5000;
 9 
10 // 解释执行
11 [Benchmark]
12 public void AScript1()
13 {
14     var script = new Script();
15     var result = script.Eval<int>(s);
16     if (result != r) throw new Exception("result error");
17 }
18 
19 // 只编译循环语句
20 [Benchmark]
21 public void AScript1_Loop()
22 {
23     var script = new Script();
24     // Z.Expressions.Eval不回写临时变量到上下文,这里也关闭变量回写功能,保持功能一致
25     script.Options.RewriteFunctions = false;
26     script.Options.RewriteVariables = false;
27     var result = script.Eval<int>(s, ECompileMode.Loop);
28     if (result != r) throw new Exception("result error");
29 }
30 
31 // 编译执行
32 [Benchmark]
33 public void AScript2_Compile()
34 {
35     var script = new Script();
36     // Z.Expressions.Eval不回写临时变量到上下文,这里也关闭变量回写功能,保持功能一致
37     script.Options.RewriteFunctions = false;
38     script.Options.RewriteVariables = false;
39     var result = script.Eval<int>(s, ECompileMode.All);
40     if (result != r) throw new Exception("result error");
41 }
42 
43 [Benchmark]
44 public void ZExpressions2()
45 {
46     Z.Expressions.EvalManager.DefaultContext.UseCache = false;
47     var result = Z.Expressions.Eval.Execute<int>(s);
48     if (result != r) throw new Exception("result error");
49 }
50 
51 // 编译缓存
52 [Benchmark]
53 public void AScript3_Cache()
54 {
55     var script = new Script();
56     // Z.Expressions.Eval不回写临时变量到上下文,这里也关闭变量回写功能,保持功能一致
57     script.Options.RewriteFunctions = false;
58     script.Options.RewriteVariables = false;
59     var result = script.Eval<int>(s, -1);
60     if (result != r) throw new Exception("result error");
61 }
62 
63 [Benchmark]
64 public void ZExpressions3()
65 {
66     Z.Expressions.EvalManager.DefaultContext.UseCache = true;
67     var result = Z.Expressions.Eval.Execute<int>(s);
68     if (result != r) throw new Exception("result error");
69 }

测试结果:

  • AScript解释执行大循环语句性能较低,编译循环语句性能有提升;
  • 编译执行模式,二者性能相差不大

3.9 LINQ Dynamic

复制代码
 1 private static readonly List<int> _list = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
 2 private static readonly IQueryable<int> _query = _list.AsQueryable();
 3 private static readonly int r = 5;
 4 
 5 [Benchmark]
 6 public void Queryable_AScript()
 7 {
 8     // 此场景一般是拼接查询条件字符串,AScript不提供编译缓存方案
 9     var result = _query.WhereScript(a => "a % 2 == 0").Count();
10     if (result != r) throw new Exception("result error");
11 }
12 
13 [Benchmark]
14 public void Queryable_ZExpressions()
15 {
16     // 此场景一般是拼接查询条件字符串,不使用缓存
17     Z.Expressions.EvalManager.DefaultContext.UseCache = false;
18     var result = _query.WhereDynamic(a => "a % 2 == 0").Count();
19     if (result != r) throw new Exception("result error");
20 }
21 
22 [Benchmark]
23 public void Queryable_ZExpressions_Cache()
24 {
25     Z.Expressions.EvalManager.DefaultContext.UseCache = true;
26     var result = _query.WhereDynamic(a => "a % 2 == 0").Count();
27     if (result != r) throw new Exception("result error");
28 }
29 
30 [Benchmark]
31 public void Enumerable_AScript()
32 {
33     // 此场景一般是拼接查询条件字符串,AScript不提供编译缓存方案
34     var result = _list.WhereScript(a => "a % 2 == 0").Count();
35     if (result != r) throw new Exception("result error");
36 }
37 
38 [Benchmark]
39 public void Enumerable_ZExpressions()
40 {
41     // 此场景一般是拼接查询条件字符串,不使用缓存
42     Z.Expressions.EvalManager.DefaultContext.UseCache = false;
43     var result = _list.WhereDynamic(a => "a % 2 == 0").Count();
44     if (result != r) throw new Exception("result error");
45 }
46 
47 [Benchmark]
48 public void Enumerable_ZExpressions_Cache()
49 {
50     Z.Expressions.EvalManager.DefaultContext.UseCache = true;
51     var result = _list.WhereDynamic(a => "a % 2 == 0").Count();
52     if (result != r) throw new Exception("result error");
53 }

测试结果:

  • 性能:AScript > Z.Expressions.Eval
  • IEnumerable扩展方法,Z.Expressions.Eval开启缓存时性能高,因为少了编译操作

四 总结

功能 AScript Z.Expressions.Eval
.NET框架 net45 netstandard2.0 netstandard2.1 net6.0 net8.0 net40 net45 netstandard2.0 netstandard2.1 net6.0 net8.0 net10.0
开源 免费MIT开源 收费
性能
内存占用
执行模式 解释执行+编译执行+编译缓存 编译执行+编译缓存
缓存版本控制 支持 不支持
Lambda 支持 支持
LINQ查询语法 支持 不支持
递归函数 支持 不支持
自定义语法 支持 不支持
  • 执行小体量脚本时,AScript的解释执行模式性能较高,适合执行碎片化临时脚本的场景;
  • 编译执行模式,两者性能相差不大,适合执行临时、大体量、大循环脚本的场景;
  • 编译缓存模式,AScript性能比Z.Expressions.Eval高,适合执行高频、脚本固定或变化少的场景;
  • AScript扩展能力强,支持多种脚本语言。

综上,AScript脚本引擎优势还是比较明显的。

相关推荐
天下无敌笨笨熊4 小时前
C#Modbus串口开发心得
开发语言·c#
CSDN_RTKLIB4 小时前
const与readonly关键字
c#
leonkay4 小时前
C# 锁机制——【2】深入原理
开发语言·后端·spring·c#·个人开发
geovindu8 小时前
CSharp: Wordcloud
开发语言·后端·c#·.net·.netcore·词云
geovindu10 小时前
CSharp: Command Pattern
开发语言·后端·c#·.net·.netcore·命令模式·行为模式
格林威13 小时前
C# 相机图像阴影校正:使用OpenCvSharp实现工业相机阴影平场校正功能
人工智能·数码相机·opencv·计算机视觉·c#·机器视觉·工业相机
俊昭喜喜里13 小时前
c#中的构造函数
开发语言·数据库·c#
hahaha601613 小时前
HLS高层次综合设计--axi之burst纠偏
开发语言·算法·fpga开发·c#
谈资一本本14 小时前
电脑全能修复大师 Gilisoft Total Repair,智能检测修复系统、应用、游戏问题!DLL、DirectX、.NET运行库损坏修复+缺失自动补齐!
游戏·电脑·.net