Dynamic Expresso 理解 C# 语法的子集(数字和字符串运算符、比较运算符、typeof、new、基本类型、Math 和 Convert 类,…)。它仅支持单语句表达式。请参阅文档以获取更多信息。
在此示例中,所有结果均使用 json 进行序列化,以便更易于阅读。
命令示例
您可以尝试以下一些表达方式:
5 + 2 * 8 / (80 + 9.9) "Hello" + " " + "world!" DateTime.Now.AddDays(30) new DateTime(2020, 2, 13) string.Format("姓名是{0}. 今天是{1}", "BIM百宝箱", DateTime.Now.ToShortDateString())
注意:如果输入1/5得到的结果为0,因为在C#中会默认为整数相除,结果还是整数。这时你输入1.0/5或者1/5.0均可得到浮点结果0.2。
以下内容请不用观看,除非你是编程爱好者:
支持的平台:.NET Core 3.1、.NET Core 5.0 及更高版本、.NET 4.6.2
Dynamic Expresso 是用 .NET Standard 2.0 编写的简单 C# 语句的解释器。 Dynamic Expresso 嵌入了自己的解析逻辑,通过将 C# 语句转换为 .NET lambda 表达式或委托来真正解释 C# 语句。
使用 Dynamic Expresso 开发人员可以创建可编写脚本的应用程序、无需编译即可执行 .NET 代码或创建动态 linq 语句。
语句是使用 C# 语言规范的子集编写的。全局变量或参数可以在表达式内注入和使用。它不生成程序集,但会动态创建表达式树。
例如,您可以计算数学表达式:
var interpreter = new Interpreter(); var result = interpreter.Eval("8 / 2 + 2");
或使用变量或参数解析表达式并多次调用它:
var interpreter = new Interpreter().SetVariable("service", new ServiceExample()); string expression = "x > 4 ? service.OneMethod() : service.AnotherMethod()"; Lambda parsedExpression = interpreter.Parse(expression, new Parameter("x", typeof(int))); var result = parsedExpression.Invoke(5);
或者为 LINQ 查询生成委托和 lambda 表达式:
var prices = new [] { 5, 8, 6, 2 }; var whereFunction = new Interpreter().ParseAsDelegate<Func<int, bool>>("arg > 5"); var count = prices.Where(whereFunction).Count();
演示
Dynamic Expresso 演示: http: //dynamic-expresso.azurewebsites.net/
特征
- 可以使用 C# 语法的子集编写表达式(有关详细信息,请参阅语法部分)
- 支持变量和参数
- 可以生成委托或 lambda 表达式
- 全套单元测试
- 与其他同类项目相比表现良好
- 部分支持泛型、参数数组和扩展方法(仅适用于隐式泛型参数检测)
- 部分支持
dynamic
(ExpandoObject
对于获取属性、方法调用和索引 (#142),请参阅 #72。DynamicObject
对于获取属性和索引,请参阅 #142) - 部分支持 lambda 表达式(默认禁用,因为它有轻微的性能损失)
- 不区分大小写的表达式(默认区分大小写)
- 能够发现给定表达式的标识符(变量、类型、参数)
- 占用空间小,生成的表达式是托管类,可以卸载并可以在单个应用程序域中执行
- 易于使用和部署,全部包含在单个程序集中,无需其他外部依赖项
- 用 .NET 标准 2.0 编写
- 适用于 .NET 4.6.1 和 .NET Core 2.0 的构建
- 开源(MIT 许可证)
返回值
您可以解析并执行 void 表达式(没有返回值),也可以返回任何有效的 .NET 类型。解析表达式时,您可以指定预期的表达式返回类型。例如你可以写:
var target = new Interpreter();
double result = target.Eval("Math.Pow(x, y) + 5",
new Parameter("x", typeof(double), 10),
new Parameter("y", typeof(double), 2));
内置解析器还可以理解任何给定表达式的返回类型,以便您可以检查表达式是否返回您期望的内容。
变量
变量可以通过方法在表达式内使用
var target = new Interpreter().SetVariable("myVar", 23); Assert.AreEqual(23, target.Eval("myVar"));
变量可以是原始类型或自定义复杂类型(类、结构、委托、数组、集合……)。
自定义函数可以使用委托变量传递,Interpreter.SetFunction
方法如下:
Func<double, double, double> pow = (x, y) => Math.Pow(x, y); var target = new Interpreter().SetFunction("pow", pow); Assert.AreEqual(9.0, target.Eval("pow(3, 2)"));
自定义表达式可以通过 using 方法传递Interpreter.SetExpression
。
参数
解析表达式可以接受一个或多个参数:
var interpreter = new Interpreter(); var parameters = new[] { new Parameter("x", 23), new Parameter("y", 7) }; Assert.AreEqual(30, interpreter.Eval("x + y", parameters));
参数可以是原始类型或自定义类型。您可以解析一次表达式并使用不同的参数值多次调用它:
var target = new Interpreter(); var parameters = new[] { new Parameter("x", typeof(int)), new Parameter("y", typeof(int)) }; var myFunc = target.Parse("x + y", parameters); Assert.AreEqual(30, myFunc.Invoke(23, 7)); Assert.AreEqual(30, myFunc.Invoke(32, -2));
内置类型和自定义类型
目前可用的预定义类型有:
Object object Boolean bool Char char String string SByte Byte byte Int16 UInt16 Int32 int UInt32 Int64 long UInt64 Single Double double Decimal decimal DateTime TimeSpan Guid Math Convert
您可以使用Interpreter.Reference
以下方法引用任何其他自定义 .NET 类型:
var target = new Interpreter().Reference(typeof(Uri)); Assert.AreEqual(typeof(Uri), target.Eval("typeof(Uri)")); Assert.AreEqual(Uri.UriSchemeHttp, target.Eval("Uri.UriSchemeHttp"));
语法和运算符
可以使用 C# 语法的子集来编写语句。您可以在此处找到支持的表达式的列表:
运算符:
类别 | 运营商 |
---|---|
基本的 | x.y f(x) a[x] new typeof |
一元 | + - ! (T)x |
乘法 | * / % |
添加剂 | + - |
关系和类型测试 | < > <= >= is as |
平等 | == != |
逻辑与 | & |
逻辑或 | | |
逻辑异或 | ^ |
条件与 | && |
条件或 | || |
有条件的 | ?: |
任务 | = |
空合并 | ?? |
遵循C# 规则(运算符优先级和结合性)尊重运算符优先级。
出于安全原因,可以禁用某些运算符(例如赋值运算符)。
特殊含义的文字
类别 | 表示 |
---|---|
常数 | true false null |
真正的文字后缀 | d f m |
整数字面量后缀 | u l ul lu |
字符串/字符 | "" '' |
字符串或字符文字内部支持以下字符转义序列:
\'
– 单引号,字符文字所需\"
– 双引号,字符串文字所需\\
– 反斜杠\0
– Unicode 字符 0\a
– 警报(字符 7)\b
– 退格键(字符 8)\f
– 换页(字符 12)\n
– 新行(字符 10)\r
– 回车符(字符 13)\t
– 水平制表符(字符 9)\v
– 垂直引号(字符 11)
动态 Expresso 还支持:
- 扩展方法
var x = new int[] { 10, 30, 4 }; var target = new Interpreter() .Reference(typeof(System.Linq.Enumerable)) .SetVariable("x", x); Assert.AreEqual(x.Count(), target.Eval("x.Count()"));
- 索引器方法(如
array[0]
) - 泛型,仅部分支持(仅隐式,不能调用显式泛型方法)
- Params 数组(请参阅 C#
params
关键字)
区分大小写/不区分大小写
默认情况下,所有表达式都被视为区分大小写(与 C# 中VARX
的 不同varx
)。可以选择使用不区分大小写的解析器。例如:
var target = new Interpreter(InterpreterOptions.DefaultCaseInsensitive); double x = 2; var parameters = new[] { new Parameter("x", x.GetType(), x) }; A ssert.AreEqual(x, target.Eval("x", parameters)); Assert.AreEqual(x, target.Eval("X", parameters));
默认类型
在 C# 中,如果数字具有小数位,则通常将其解释为整数或双精度数。
在某些情况下,如果没有指定特定后缀,能够配置默认的数字类型可能会很有用:例如在财务计算中,通常数字被解释为十进制类型。
在这些情况下,您可以使用 方法设置默认号码类型Interpreter.SetDefaultNumberType
。
var target = new Interpreter(); target.SetDefaultNumberType(DefaultNumberType.Decimal); Assert.IsInstanceOf(typeof(System.Decimal), target.Eval("45")); Assert.AreEqual(10M/3M, target.Eval("10/3")); // 3.33333333333 instead of 3
限制
并非所有 C# 语法都受支持。以下是不支持的功能的一些示例:
- 多行表达式
- for/foreach/while/do 运算符
- 数组/列表/字典初始化
- 显式泛型调用(如
method<type>(arg)
) - Lambda/委托声明(仅支持委托和 lamda 作为变量或参数或作为表达式的返回类型)
- 数组/列表/字典元素赋值(设置索引器运算符)
- 对对象的其他操作
dynamic
(现在仅支持属性、方法调用和索引)
评论0