Supported platforms: .NET Core 3.1, .NET Core 5.0 and above, .NET 4.6.2
Dynamic Expresso is an interpreter for simple C# statements written in .NET Standard 2.0.
Dynamic Expresso 是用 .NET Standard 2.0 编写的简单 C# 语句的解释器。
Dynamic Expresso embeds its own parsing logic, really interprets C# statements by converting it to .NET lambda expressions or delegates.
Dynamic Expresso 嵌入了自己的解析逻辑,通过将 C# 语句转换为 .NET lambda 表达式或委托来真正解释 C# 语句。
Using Dynamic Expresso developers can create scriptable applications, execute .NET code without compilation or create dynamic linq
statements.
使用 Dynamic Expresso 开发人员可以创建可编写脚本的应用程序,无需编译即可执行 .NET 代码或创建动态 linq 语句。
Statements are written using a subset of C# language specifications. Global variables or parameters can be injected and used inside
expressions. It doesn’t generate assembly but it creates an expression tree on the fly.
语句是使用 C# 语言规范的子集编写的。全局变量或参数可以在表达式中注入和使用。它不会生成程序集,但会动态创建表达式树。
例如,您可以评估数学表达式:
For example you can evaluate math expressions:
var interpreter = new Interpreter();
var result = interpreter.Eval("8 / 2 + 2");
或解析带有变量或参数的表达式并多次调用它:
or parse an expression with variables or parameters and invoke it multiple times:
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 表达式:
or generate delegates and lambda expressions for LINQ queries:
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 live demo: /
Dynamic Expresso is available on [NuGet]. You can install the package using:
PM> Install-Package DynamicExpresso.Core
Source code and symbols (.pdb files) for debugging are available on [Symbol Source].
dynamic
(ExpandoObject
for get properties, method invocation and indexes(#142), see #72. DynamicObject
for get properties and indexes, see #142)You can parse and execute void expression (without a return value) or you can return any valid .NET type.
When parsing an expression you can specify the expected expression return type. For example you can write:
var target = new Interpreter();
double result = target.Eval<double>("Math.Pow(x, y) + 5",new Parameter("x", typeof(double), 10),new Parameter("y", typeof(double), 2));
The built-in parser can also understand the return type of any given expression so you can check if the expression returns what you expect.
Variables can be used inside expressions with Interpreter.SetVariable
method:
var target = new Interpreter().SetVariable("myVar", 23);Assert.AreEqual(23, target.Eval("myVar"));
Variables can be primitive types or custom complex types (classes, structures, delegates, arrays, collections, …).
Custom functions can be passed with delegate variables using Interpreter.SetFunction
method:
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)"));
Custom Expression can be passed by using Interpreter.SetExpression
method.
Parsed expressions can accept one or more parameters:
var interpreter = new Interpreter();var parameters = new[] {new Parameter("x", 23),new Parameter("y", 7)
};Assert.AreEqual(30, interpreter.Eval("x + y", parameters));
Parameters can be primitive types or custom types. You can parse an expression once and invoke it multiple times with different parameter values:
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));
Either a variable or a parameter with name this
can be referenced implicitly.
class Customer { public string Name { get; set; } }var target = new Interpreter();// 'this' is treated as a special identifier and can be accessed implicitly
target.SetVariable("this", new Customer { Name = "John" });// explicit context reference via 'this' variable
Assert.AreEqual("John", target.Eval("this.Name"));// 'this' variable is referenced implicitly
Assert.AreEqual("John", target.Eval("Name"));
Currently predefined types available are:
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
You can reference any other custom .NET type by using Interpreter.Reference
method:
var target = new Interpreter().Reference(typeof(Uri));Assert.AreEqual(typeof(Uri), target.Eval("typeof(Uri)"));
Assert.AreEqual(Uri.UriSchemeHttp, target.Eval("Uri.UriSchemeHttp"));
You can use the Interpreter.ParseAsDelegate<TDelegate>
method to directly parse an expression into a .NET delegate type that can be normally invoked.
In the example below I generate a Func<Customer, bool>
delegate that can be used in a LINQ where expression.
您可以使用该Interpreter.ParseAsDelegate方法将表达式直接解析为可以正常调用的 .NET 委托类型。在下面的示例中,我生成了一个Func<Customer, bool>可以在 LINQ where 表达式中使用的委托。
class Customer
{public string Name { get; set; }public int Age { get; set; }public char Gender { get; set; }
}[Test]
public void Linq_Where()
{var customers = new List<Customer> {new Customer() { Name = "David", Age = 31, Gender = 'M' },new Customer() { Name = "Mary", Age = 29, Gender = 'F' },new Customer() { Name = "Jack", Age = 2, Gender = 'M' },new Customer() { Name = "Marta", Age = 1, Gender = 'F' },new Customer() { Name = "Moses", Age = 120, Gender = 'M' },};string whereExpression = "customer.Age > 18 && customer.Gender == 'F'";var interpreter = new Interpreter();Func<Customer, bool> dynamicWhere = interpreter.ParseAsDelegate<Func<Customer, bool>>(whereExpression, "customer");Assert.AreEqual(1, customers.Where(dynamicWhere).Count());
}
This is the preferred way to parse an expression that you known at compile time what parameters can accept and what value must return.
You can use the Interpreter.ParseAsExpression<TDelegate>
method to directly parse an expression into a .NET lambda expression (Expression<TDelegate>
).
您可以使用该Interpreter.ParseAsExpression方法直接将表达式解析为 .NET lambda 表达式 ( Expression)。
In the example below I generate a Expression<Func<Customer, bool>>
expression that can be used in a Queryable LINQ where expression or in any other place where an expression is required. Like Entity Framework or other similar libraries.
在下面的示例中,我生成了一个Expression<Func<Customer, bool>>表达式,该表达式可用于 Queryable LINQ where 表达式或任何其他需要表达式的地方。像实体框架或其他类似的库。
class Customer
{public string Name { get; set; }public int Age { get; set; }public char Gender { get; set; }
}[Test]
public void Linq_Queryable_Expression_Where()
{IQueryable<Customer> customers = (new List<Customer> {new Customer() { Name = "David", Age = 31, Gender = 'M' },new Customer() { Name = "Mary", Age = 29, Gender = 'F' },new Customer() { Name = "Jack", Age = 2, Gender = 'M' },new Customer() { Name = "Marta", Age = 1, Gender = 'F' },new Customer() { Name = "Moses", Age = 120, Gender = 'M' },}).AsQueryable();string whereExpression = "customer.Age > 18 && customer.Gender == 'F'";var interpreter = new Interpreter();Expression<Func<Customer, bool>> expression = interpreter.ParseAsExpression<Func<Customer, bool>>(whereExpression, "customer");Assert.AreEqual(1, customers.Where(expression).Count());
}
Statements can be written using a subset of the C# syntax. Here you can find a list of the supported expressions:
可以使用 C# 语法的子集编写语句。在这里您可以找到支持的表达式列表:
Supported operators:
项目 | Value
Category | Operators |
---|---|
Primary | x.y f(x) a[x] new typeof |
Unary | + - ! (T)x |
Multiplicative | * / % |
Additive | + - |
Relational and type testing | < > <= >= is as |
Equality | == != |
Logical AND | & |
Logical OR | | |
Logical XOR | ^ |
Conditional AND | && |
Conditional OR | || |
Conditional | ?: |
Assignment | = |
Null coalescing | ?? |
Operators precedence is respected following C# rules (Operator precedence and associativity).
运算符优先级遵循C# 规则(运算符优先级和关联性)。
Some operators, like the assignment operator, can be disabled for security reason.
Category | Operators |
---|---|
Constants | true false null |
Real literal suffixes | d f m |
Integer literal suffixes | u l ul lu |
String/char | "" '' |
The following character escape sequences are supported inside string or char literals:
'
- single quote, needed for character literals"
- double quote, needed for string literals\
- backslash