一个精简的C#表达式执行框架Dynamic Expresso

一、简介

Dynamic Expresso是一个用.NET Standard 2.0编写的简单c#语句的解释器。Dynamic Expresso嵌入了自己的解析逻辑,通过将其转换为.NET lambda表达式或委托来解释c#语句。

使用Dynamic Expresso开发人员可以创建可编写脚本的应用程序,无需编译即可执行.NET代码,或者创建动态linq语句。

语句是使用c#语言规范的子集编写的。全局变量或参数可以被注入并在表达式中使用。它不会生成程序集,但会动态地创建表达式树。

二、安装

新建控制台项目DynamicExpressoResearch,并通过NUGet添加DynamicExpresso.Core;

三、功能和特性

  1. 返回值

    你可以解析并执行一个void类型没有返回值的表达式,或者也可以返回任何有效的.NET类型。我们可以在解析表达式的时候指定期望的返回类型。

static void EvalVoidExp(){var target = new Interpreter();var t = target.Eval("Program.empty()", new Parameter("Program", typeof(Program), new Program()));if (t == null){Console.WriteLine("EvalVoidExp return value is null");}}//EvalVoidExp return value is null
static void EvalSpecReturnTypeExp(){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));Console.WriteLine(string.Format("We specify the return value type of the expression as double, return value is {0}, value type is {1}", result, result.GetType().FullName));int r = target.Eval<int>("Math.Pow(x, y) + 5",new Parameter("x", typeof(int), 10),new Parameter("y", typeof(int), 2));Console.WriteLine(string.Format("We specify the return value type of the expression as int, return value is {0}, value type is {1}", r, r.GetType().FullName));}//We specify the return value type of the expression as double, return value is 105, value type is System.Double//We specify the return value type of the expression as int, return value is 105, value type is System.Int32
同时内置的表达式parser也可以自动感知表达式的返回类型;
static void AutoInferDataType(){var target = new Interpreter();object r = target.Eval("Math.Pow(x, y) + 5",new Parameter("x", typeof(int), 10),new Parameter("y", typeof(int), 2));Console.WriteLine(string.Format("We do not  specify the return value type of the expression, return value is {0}, value type is {1}", r, r.GetType().FullName));}

2.变量(Variables)

变量依附在Interpreter上,相当于一个全局的控制参数,可以应用到同一个Interpreter实例的所有表达式中;
Interpreter提供了不同的方法可以实现传入不同类型的变量;
SetVariable可以内置的原始类型及复杂的自定义数据类型;
static void SetParameter(){var target = new Interpreter();target.SetVariable("rate", 0.8);object r = target.Eval("price * rate",new Parameter("price", typeof(int), 200));Console.WriteLine(string.Format("The price of 200 with a 20% discount is {0}", r));r = target.Eval("price * rate",new Parameter("price", typeof(int), 499));Console.WriteLine(string.Format("The price of 499 with a 20% discount is {0}", r));}The price of 200 with a 20% discount is 160The price of 499 with a 20% discount is 399.2
SetFunction可以通过委托的方式传递函数
static void SetFunction(){var target = new Interpreter();Func<double, double, double> pow = (x, y) => Math.Pow(x, y);target.SetFunction("pow", pow);object r = target.Eval("pow(x, y)",new Parameter("x", typeof(int), 10),new Parameter("y", typeof(int), 2));Console.WriteLine(string.Format("10 to the second power  is {0}", r));r = target.Eval("pow(x, y)",new Parameter("x", typeof(int), 2),new Parameter("y", typeof(int), 4));Console.WriteLine(string.Format("2 to the fourth power is {0}", r));}//10 to the second power  is 100//2 to the fourth power is 16
SetExpression可以设置自定义的Expression
static void SetExpression(){var target = new Interpreter();var rateExp = Expression.Constant(0.8);target.SetExpression("rate", rateExp);object r = target.Eval("price * rate",new Parameter("price", typeof(int), 200));Console.WriteLine(string.Format("The price of 200 with a 20% discount is {0}", r));r = target.Eval("price * rate",new Parameter("price", typeof(int), 499));Console.WriteLine(string.Format("The price of 499 with a 20% discount is {0}", r));}

3.参数(Parameters)

参数需要每次执行的时候传递,参数可以是任意的类型,我们可以只解析一次表达式,并通过不同的参数进行多次调用;
static void Invoke(){var target = new Interpreter();var parameters = new[] {new Parameter("x", typeof(int)),new Parameter("y", typeof(int))};var myFunc = target.Parse("x + y", parameters);myFunc.Invoke(23, 7);myFunc.Invoke(32, -2);            }

4.内置类型和自定义类型

默认内部直接支持的数据类型有
Object object Boolean bool Char charString stringSByte Byte byteInt16 UInt16 Int32 int UInt32 Int64 long UInt64 Single Double double Decimal decimal DateTime TimeSpanGuidMath Convert
我们可以直接使用Interpreter.Reference来引用任何的.net类型
static void ReferenceType(){var target = new Interpreter().Reference(typeof(Uri));Console.WriteLine((target.Eval("typeof(Uri)") as Type).FullName);Console.WriteLine(target.Eval("Uri.UriSchemeHttp"));}//System.Uri//http

5.生成动态委托

我们可以直接使用Interpreter.ParseAsDelegate<TDelegate>来解析表达式生成对应的委托,然后可以直接调用对应的委托。
class Customer{public string Name { get; set; }public int Age { get; set; }public char Gender { get; set; }}static void DynamicDelegate(){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' && index >=0";var interpreter = new Interpreter();Func<Customer, int, bool> dynamicWhere = interpreter.ParseAsDelegate<Func<Customer, int, bool>>(whereExpression, "customer", "index");Console.WriteLine(customers.Where(dynamicWhere).Count());}

6.生成lambda表达式

我们可以使用Interpreter.ParseAsExpression<TDelegate>解释表达式直接生成lambda表达式;
static void DynamicLambdaExpress(){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' },}.AsQueryable();string whereExpression = "customer.Age > 18 && customer.Gender == 'F' && index >=0";var interpreter = new Interpreter();Expression<Func<Customer, int, bool>> dynamicWhere = interpreter.ParseAsExpression<Func<Customer, int, bool>>(whereExpression, "customer", "index");Console.WriteLine(customers.Where(dynamicWhere).Count());}

7.支持的操作符

CategoryOperators
Primaryx.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??

8.文本标识

CategoryOperators
Constantstrue false null
Real literal suffixesd f m
Integer literal suffixesu l ul lu
String/char"" ''

字符串或者字符类型中支持的转译

  • ' - single quote, needed for character literals

  • " - double quote, needed for string literals

  • \ - backslash

  • \0 - Unicode character 0

  • \a - Alert (character 7)

  • \b - Backspace (character 8)

  • \f - Form feed (character 12)

  • \n - New line (character 10)

  • \r - Carriage return (character 13)

  • \t - Horizontal tab (character 9)

  • \v - Vertical quote (character 11)

9.调用对象成员

可以在表达式中直接调用对象实例的成员
public class Student{public string Name { get; set; }public int Age { get; set; }public void Hello(){Console.WriteLine(string.Format("hello, my name is {0}, my age is {1}", Name, Age));}public static Student Instance(){return new Student() { Name="auto", Age = 0};}}static void InvokTypeMember(){var s = new Student() { Name="mango", Age = 30};var target = new Interpreter().SetVariable("s", s);target.Reference(typeof(Student));Console.WriteLine(target.Eval("s.Hello()"));Console.WriteLine(target.Eval("s.Name"));Console.WriteLine(target.Eval("new Student().Hello()"));Console.WriteLine(target.Eval("Student.Instance().Hello()"));}
同时可以支持扩展方法的调用;
支持数组索引的调用;
支持params数组;
部分支持泛型;
static void InvokCollectionMember(){var x = new int[] { 10, 30, 4 };          var target = new Interpreter();target.SetVariable("x", x);Console.WriteLine(target.Eval("x.Count()"));//Console.WriteLine(target.Eval("x.Count<int>()"));Console.WriteLine(target.Eval("x[0]"));var s = new string[] {"mango", "is","test","params" };target.SetVariable("s", s);Console.WriteLine(target.Eval("string.Join(\" \", s)"));}

10.Lambda表达式

Dynamic Expresso只支持lambda表达式的部分功能;为了减少对性能的影响,默认是不开启Lambda表达式的解析的;我们可以通过InterpreterOptions.LambdaExpressions 来启用这个功能;
static void EvalAsLambda(){var x = new string[] { "this", "is", "awesome" };var options = InterpreterOptions.Default | InterpreterOptions.LambdaExpressions; // enable lambda expressionsvar target = new Interpreter(options).SetVariable("x", x);var results = target.Eval<IEnumerable<string>>("x.Where(str => str.Length > 5).Select(str => str.ToUpper())");Console.WriteLine(results.First());}

11.大小写

默认情况下表达式是区分大小写的,可以通过InterpreterOptions.CaseInsensitive或者InterpreterOptions.DefaultCaseInsensitive来忽略大小写;
static void IgnorCase(){var target = new Interpreter(InterpreterOptions.CaseInsensitive);double x = 2;var parameters = new[] {new Parameter("x", x.GetType(), x)};Console.WriteLine(target.Eval("x", parameters));Console.WriteLine(target.Eval("X", parameters));}

12.标识符探测

Dynamic Expresso支持使用Interpreter.DetectIdentifiers来获取表达式中的变量标识符。
static void DetectIdentifier(){var target = new Interpreter();var detectedIdentifiers = target.DetectIdentifiers("x + y");Console.WriteLine(detectedIdentifiers.UnknownIdentifiers.First());Console.WriteLine(detectedIdentifiers.UnknownIdentifiers.Last());}

13.默认数字类型

默认情况下数字会被解析成int类型或者double类型;但是在有些情况下,例如财务中需要使用decimal类型的数字;我们可以使用Interpreter.SetDefaultNumberType来设置;
static void SetNumberType(){var target = new Interpreter();target.SetDefaultNumberType(DefaultNumberType.Decimal);Console.WriteLine(target.Eval("45").GetType().FullName);Console.WriteLine(target.Eval("10/3"));}

14.异常

提供了ParseException作为解析表达式异常的基类,同时提供了数个不同的具体的异常类,例如UnknownIdentifierException, NoApplicableMethodException;

15.多线程

Interpreter类可以在多线程中使用,但是需要确保在多线程中只能调用get属性和Parse及Eval这些方法,而对于初始化的一些设置(SetVariable/Reference)等只能在实例化对象的阶段调用;如果我们需要使用不同的参数多次执行表达式,最好的方式就是解析一次表达式,并调用多次解析的结果;

16.安全性

表达式中只能访问Reference中设置的类型以及设置的变量和参数的对象实例;我们需要慎重考虑暴露哪些类给用户;从1.3版本其默认禁用直接调用除了Type.Name的反射类方法;

17.功能局限

Not every C# syntaxes are supported. Here some examples of NOT supported features:

  • Multiline expressions

  • for/foreach/while/do operators

  • Array/list/dictionary initialization

  • Explicit generic invocation (like method

    (arg))
  • Lambda/delegate declaration (delegate and lamda are only supported as variables or parameters or as a return type of the expression)

  • Array/list/dictionary element assignment (set indexer operator)

  • Other operations on dynamic objects (only property, method invocation and index now are supported)

18.使用场景

  • Programmable applications

  • Allow the user to inject customizable rules and logic without recompiling

  • Evaluate dynamic functions or commands

  • LINQ dynamic query

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/286592.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

算法马拉松13 A-E解题报告

A题意&#xff08;取余最长路&#xff09;: 佳佳有一个n*m的带权矩阵&#xff0c;她想从(1,1)出发走到(n,m)且只能往右往下移动&#xff0c;她能得到的娱乐值为所经过的位置的权的总和。 有一天&#xff0c;她被下了恶毒的诅咒&#xff0c;这个诅咒的作用是将她的娱乐值变为对p…

Modis数据处理工具:MRT百度网盘下载和手把手图文安装教程

如下图所示为 MODIS Reprojection Tool(MRT)的软界面,看似简单,却是Modis遥感影像必不可少的处理工具,如投影变换等。本文以图文并茂的形式,详细讲解MRT软件在Windows10平台上的安装过程,并附MRT软件百度网盘下载地址。 Modis Tool主界面: 一、安装过程 1、安装Jav…

Word中如何设置图片与段落的间距为半行

第一种&#xff1a; 正文为5号&#xff0c;那么图片或者Viso对象前后空一行&#xff0c;设置字号为7号或者更小&#xff0c;这样设置的间距就是那个7号字的间距&#xff0c;比5号小&#xff0c;看着空白不是那么大。 第二种&#xff1a; Visio对象转为jpg&#xff0c;然后选中图…

在微信小程序中使用“随机键盘”

最近研究微信小程序&#xff0c;发现在手机上使用系统键盘非常不方便&#xff0c;一是按键太小&#xff0c;对于小学生来说&#xff0c;操作非常不方便&#xff1b;二是系统键盘反复切换影响界面布局。于是自己决定自己写一个随机的小键盘。 原理非常简单&#xff1a;拿“口算练…

Android之提示订阅配置订阅需要传新的包 添加结算权限。

1 问题 apk上google应用市场&#xff0c;然后开通支付商品&#xff0c;错误提示如下 2 解决办法 AndroidManifest.xml里面添加谷歌支付权限 <!-- google pay --><uses-permission android:name"com.android.vending.BILLING" />

【前端就业课 第一阶段】HTML5 零基础到实战(三)一篇文CSS基础入门

注意&#xff1a;手机&#xff08;APP&#xff09;打开&#xff0c;内容显示更佳&#xff0c;不会的私聊博主即可 想要拿代码或加入学习计划&#xff08;** 博主会监督你并且教你写文章 **&#xff09;的拉到最下面&#xff08;PC端Web打开&#xff09;加博主即可&#xff0c;目…

C#如何获取实体类属性名和值?

数据模型定义public class User{public User(){student new student();}public string name { get; set; }public string gender { get; set; }public int age { get; set; }public student student { get; set; }}public class student{public int ID { get; set; }public st…

将VNC 安装在Centos 7步骤

&#xff08; Virtual Network Computing&#xff09;VNC允许Linux系统可以类似实现像Windows中的远程桌面访问那样访问Linux桌面。本文配置机器是兴宁市网络信息中心的一台Centos 7 HP服务器环境下运行。 首先试试服务器装了VNC没 [rootwic ~]# rpm -q tigervnc tigervnc-serv…

利用MRT进行Modis NDVI数据(MOD13Q1)投影变换格式转换操作图文教程

本实例以Modis NDVI(MOD13Q1,空间分辨率为250m)一景影像数据为例,演示利用MRT进行Modis NDVI影像变换,主要内容包括:将.hdf格式转为.tif格式,将坐标系转为Albers等积投影。 ArcGIS完美转换方法: 《ArcGIS10.8完美实现MODIS NDVI数据格式转换和投影变换》 《重磅!ArcGI…

ActiveMQ无法启动

解决办法&#xff1a;activemq无法启动&#xff0c;端口被占用 用netstat -an无法查出61616被哪个进程占用&#xff08;实践证明&#xff0c;netstat -ano|findstr 61616什么也没有找到&#xff09; 经过排查和网上资料参考&#xff0c;被windows的Internet connection share(I…

Android之升级OkHttp编译提示错误如下Using ‘body(): ResponseBody?’ is an error. moved to val

1 问题 升级okHttp库&#xff0c;编译项目错误如下 Using ‘body(): ResponseBody?’ is an error. moved to val 2 解决办法 原来的代码 val list response.body().string() 去掉&#xff08;&#xff09;就可以了 val list response.body.string()

单例

当实际上Singleton是一个对象&#xff0c;我们不能保证使用者不会使用其他的方法去创建&#xff08;比如alloc&#xff09;,这个时候他就会创建多个实例&#xff0c;这样就会出现这些无法感知的bug&#xff09; implementation Singleton static Singleton * sharedSingleton …

Google 开源的 Android 排版库:FlexboxLayout

最近Google开源了一个项目叫「FlexboxLayout」。1.什么是 Flexbox简单来说 Flexbox 是属于web前端领域CSS的一种布局方案&#xff0c;是2009年W3C提出了一种新的布局方案&#xff0c;可以简便、完整、响应式地实现各种页面布局&#xff0c;并且 React Native 也是使用的 Flex 布…

Docker Network 配置,自定义bridge网络

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182Docker Network 配置&#xff0c;自定义bridge网络 1.停止服务 service docker stop 2.关掉docker0 ifconfi…

再见 KataCoda——O'Reilly 宣布其将在六月份关闭

近日听闻 OReilly 将永久关闭在线学习网站 KataCoda&#xff0c;对于广大程序员和学习者来说&#xff0c;这无疑是一件痛心疾首的事情&#xff0c;以后我们再也看不到那只会变成的功夫猫了。KataCoda 简介KataCoda 成立于 2016 年&#xff0c;它是一个在线学习平台&#xff0c;…

中国区域Modis行列号(附Shapefile文件下载)

重磅&#xff1a;Landsat中国西北地区行列号Shapefile图层对照&#xff08;附行列号Shapefile下载&#xff09; 全球&#xff1a; 中国&#xff1a;

Android之解决webview加载第三方网页点击弹不出下拉框(html页面里面的select标签)

1 问题 决webview加载第三方网页点击弹不出下拉框&#xff08;html页面里面的select标签&#xff09;&#xff0c;我们访问youtube.com官网&#xff0c;点击网站的视频&#xff0c;点击视频右上角三个点设置&#xff0c;然后点击 播放设置 然后点击画质 弹不出选项框&#xf…

【前端就业课 第一阶段】HTML5 零基础到实战(四)伪类与伪元素

注意&#xff1a;手机&#xff08;APP&#xff09;打开&#xff0c;内容显示更佳&#xff0c;不会的私聊博主即可 想要拿代码或加入学习计划&#xff08;** 博主会监督你并且教你写文章 **&#xff09;的拉到最下面&#xff08;PC端Web打开&#xff09;加博主即可&#xff0c;目…

编写第一个响应式页面

2019独角兽企业重金招聘Python工程师标准>>> 本文为大家讲解如何使用一种科学的方法实现网页设计&#xff0c;从原理上搞清楚什么是响应式设计&#xff0c;并实现一个简易的响应式设计框架&#xff0c;以此为基础&#xff0c;编写出第一个响应式页面。 不知道现在大…

container 的背后

如果要看laravel的单个功能的源代码&#xff0c;首先去找对应得ServiceProvider,例如加密功能hash,则按一下步骤查看源代码&#xff1a; HashServiceProvider.php(主要是看register方法) singleton()方法就是将BcryptHasher这个类实例化一次&#xff0c;然后在哪里都可以用&…