在C#开发中使用第三方组件LambdaParser、DynamicExpresso、Z.Expressions, ...

打印 上一主题 下一主题

主题 600|帖子 600|积分 1800

在进行项目开发的时候,刚好需要用到对字符串表达式进行求值的处理场景,因此寻找了几个符合要求的第三方组件LambdaParser、DynamicExpresso、Z.Expressions,它们各自功能有所不同,不过基本上都能满足要求。它们都可以根据相关的参数进行字符串表达式的求值,本篇随笔介绍它们三者的使用代码,以及总结其中的一些经验。
数学表达式求值应该是最常见的,一般我们在应用程序中如果需要计算,是需要对参数进行类型转换,然后在后台进行相应计算的。但是如果是计算一些符合的式子或者公式,特别是参数不一定的情况下,这个就比较麻烦。利用第三方组件,对表达式进行快速求值,可以满足我们很多实际项目上的需求,而且处理起来也很方便。
这几个第三方组件,它们的GitHub或官网地址:
https://github.com/nreco/lambdaparser
https://github.com/dynamicexpresso/DynamicExpresso 
https://eval-expression.net/eval-execute
不过Z.Expressions是收费的,前两者都是免费的。
 
我使用字符串表达式进行求值的场景,主要就是想对一个SQL条件的表达式,转换为普通的字符串表达式,然后根据对象的参数值,进行求值处理,这几个表达式求值组件都支持这样的操作,为了更好演示它们的使用效果及代码,我们专门创建了一个案例代码进行测试验证,确认满足我的实际需求。

  
1、Z.Expressions.Eval 表达式解析

Z.Expression.Eval是一个免费开源的(后续收费了),可扩展的,超轻量级的公式化语言解析执行工具包,可以在运行时解析C#表达式的开源免费组件。Z.Expressions从2.0开始支持了NetCore,但是收费的。参考地址:https://riptutorial.com/eval-expression/learn/100000/getting-started 或者 https://eval-expression.net/eval-execute
在运行时解析C#表达式,例如一些工资或者成本核算系统,就需要在后台动态配置计算表达式,从而进行计算求值。
下面对几个不同的案例代码进行介绍及输出结果验证
 匿名类型处理
  1. //匿名类型
  2. string expression = "a*2 + b*3 - 3";
  3. int result = Eval.Execute<int>(expression, new { a = 10, b = 5 });
  4. Console.WriteLine("{0} = {1}", expression, result); //a*2 + b*3 - 3 = 32
复制代码
指定参数
  1. //指定参数
  2. expression = "{0}*2 + {1}*3 - 3";
  3. result = Eval.Execute<int>(expression, 10, 5);
  4. Console.WriteLine("{0} = {1}", expression, result);//{0}*2 + {1}*3 - 3 = 32
复制代码
类对象
  1. //类对象
  2. expression = "a*2 + b*3 - 3";
  3. dynamic expandoObject = new ExpandoObject();
  4. expandoObject.a = 10;
  5. expandoObject.b = 5;
  6. result = Eval.Execute<int>(expression, expandoObject);
  7. Console.WriteLine("{0} = {1}", expression, result); //a*2 + b*3 - 3 = 32
复制代码
字典对象
  1. //字典对象
  2. expression = "a*2 + b*3 - 3";
  3. var values = new Dictionary<string, object>()
  4. {
  5.     { "a", 10 },
  6.     { "b", 5 }
  7. };
  8. result = Eval.Execute<int>(expression, values);
  9. Console.WriteLine("{0} = {1}", expression, result);//a*2 + b*3 - 3 = 32
复制代码
委托类型
  1. //委托类型1
  2. expression = "{0}*2 + {1}*3";
  3. var compiled = Eval.Compile<Func<int, int, int>>(expression);
  4. result = compiled(10, 15);
  5. Console.WriteLine("{0} = {1}", expression, result);//{0}*2 + {1}*3 = 65
  6. //委托类型2
  7. expression = "a*2 + b*3";
  8. compiled = Eval.Compile<Func<int, int, int>>(expression, "a", "b");
  9. result = compiled(10, 15);
  10. Console.WriteLine("{0} = {1}", expression, result);//a*2 + b*3 = 65
复制代码
字符串扩展支持
  1. //字符串扩展支持-匿名类型
  2. expression = "a*2 + b*3 - 3";
  3. result = expression.Execute<int>(new { a = 10, b = 5 });
  4. Console.WriteLine("{0} = {1}", expression, result);//a*2 + b*3 - 3 = 32
  5. //字符串扩展支持-字典类型
  6. expression = "a*2 + b*3 - 3";
  7. values = new Dictionary<string, object>()
  8. {
  9.     { "a", 10 },
  10.     { "b", 5 }
  11. };
  12. result = expression.Execute<int>(values);
  13. Console.WriteLine("{0} = {1}", expression, result);//a*2 + b*3 - 3 = 32
复制代码
可以看出,该组件提供了非常丰富的表达式运算求值处理方式。
 
2、NReco.LambdaParser 表达式解析

我看中这个组件的处理,主要是因为它能够传入参数是字典类型,这样我可以非常方便的传入各种类型的参数,并且这个组件比较接近SQL语法,可以设置利用常规的=代替表达式的==,这样对于SQL语句来说是方便的。
它的案例代码如下所示。
  1. /// <summary>
  2. /// NReco.LambdaParser 表达式解析
  3. /// </summary>
  4. private void btnLamdaParser_Click(object sender, EventArgs e)
  5. {
  6.     var lambdaParser = new NReco.Linq.LambdaParser();
  7.     var dict = new Dictionary<string, object>();
  8.     dict["pi"] = 3.14M;
  9.     dict["one"] = 1M;
  10.     dict["two"] = 2M;
  11.     dict["test"] = "test";
  12.     Console.WriteLine(lambdaParser.Eval("pi>one && 0<one ? (1+8)/3+1*two : 0", dict)); // --> 5
  13.     Console.WriteLine(lambdaParser.Eval("test.ToUpper()", dict)); // --> TEST
  14.     Console.WriteLine(lambdaParser.Eval("pi>one && 0<one ", dict)); // --> True
  15.     Console.WriteLine(lambdaParser.Eval("test.ToUpper()", dict)); // --> TEST
  16. }
复制代码
对于类的属性表达式查询,测试代码如下所示
  1.     var lambdaParser = new LambdaParser();
  2.     lambdaParser.AllowSingleEqualSign = true;//可以使用 = 作为逻辑判断,如Title ="Leader",而不用Title =="Leader"
  3.     var evalResult = lambdaParser.Eval(repalce, dict);
复制代码
 
4、SQL条件语句的正则表达式和字符串求值处理

前面介绍了几个表达式求值处理的组件,他们基本上都能够满足实际的求值处理,只是提供的功能有所侧重。
我主要希望用它来对特定的表达式进行求布尔值,判断表达式是否满足条件的。
例如对于sql条件语句:(Amount> 500 and Title ='Leader') or Age> 32, 以及一个字典对象的参数集合,我希望能够提取里面的Amount、Title、Leader、Age这样的键,然后给字典赋值,从而判断表达式的值。
由于sql表达式和C#代码的表达式逻辑语法有所差异,我们需要替换and Or 为实际的&& || 字符,因此给定替换的正则表达式:\sand|\sor

而我需要先提取条件语句的键值内容,然后获得指定的键参数,那么也要提供一个正则表达式:\w*[^>= \" 操作符后内容:");            this.txtContent.AppendText(Environment.NewLine);            this.txtContent.AppendText(repalce);        }[/code]
  1. var interpreter = new<strong> Interpreter</strong>();
  2. var result = interpreter.Eval("8 / 2 + 2");
复制代码
表达式处理结果如下所示

 它的逻辑代码如下。
  1. var target = new<strong> Interpreter</strong>();
  2. double result = target.Eval<double>("Math.Pow(x, y) + 5",
  3.      new Parameter("x", typeof(double), 10),
  4.      new Parameter("y", typeof(double), 2));
复制代码
这样我们就可以转换SQL条件表达式为实际的C#表达式,并通过赋值参数,实现动态表达式的求值处理。
 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

兜兜零元

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表