c#: 表达式树的简化

环境:

  • .net 6

一、问题?

有下面的表达式:

var nums = new List<int> {
    1, 2, 3 };
Expression<Func<int, bool>> exp = i => i > nums.Max();

我们知道,它其实就是:exp = i => i > 3;
那么问题是,我们如何将它改造成这样呢?

在orm解析lambda生成sql时,也经常遇到这样的窘境:

var scores = new List<Person> {
    new Person {
    Id = 1, Score = 60 } };
var sql = orm.Select<Person>().Where(i => i.Score > scores.Select(i=>i.Score).Max() || i.Score == 100).ToSql();
//error: 
// System.Exception:“未实现函数表达式 value(Program+<>c__DisplayClass0_0).scores.Select(i => i.Score).Max() 解析,如果正在操作导航属性集合,请使用 .AsSelect().Max()”


public class Person
{
   
    public int Id {
    get; set; }
    public double Score {
    get; set; }
}

所以,就有了个想法:能不能对表达式进行简化呢?
就比如上面的可以改造成:orm.Select<Person>().Where(i => i.Score > 60 || i.Score == 100).ToSql();

二、表达式树简化原理

lambda表达式是表达式树的根, 它可能会有参数列表, 其子孙节点可能会引用这些参数, 也可能没有引用, 将没有引用的分支编译求值, 将结果再“放回”表达式中即可!

还是以下面的表达式为例:

var nums = new List<int> {
    1, 2, 3 };
Expression<Func<int, bool>> exp = i => i > nums.Max();

在节点 > 的右侧 nums.Max() 没有引用参数列表, 那么它就可以被简化, 简化后就是:
exp = i => i > 3;

再看如下:

var nums = new List<int> {
    1, 2, 3 };
Expression<Func<int, bool>> expr = i => i > nums.Max() || nums.Count > 0;

我们不仅可以将 || 右侧的简化为 true, 还可以根据||的短路特性对整体进行简化, 结果如下:
exp = i => true

三、表达式树的树状图

我们知道有各种各样的表达式类型, 如: +-/*Call/MemberInit等。
无论哪种类型, 都可以将它们抽象成一棵树, 如:
Call类型的表达式, 可以看成:

在这里插入图片描述

表达式的嵌套:
lambda表达式有可能会嵌套lambda, 如:

var nums = new List<int> {
    1, 2, 3 };
Expression<Func<int>> expr = () => Filter(nums, i => i > 1);

static int Filter(List<int> nums, Func<int, bool> func)
{
   
    return nums.First(i => func(i));
}

它的结构树如下:
在这里插入图片描述

这个是函数接受委托的, 还有函数接受lambda的,如:

var nums = new List<int> {
    1, 2, 3 };
Expression<Func<int>> expr = () => Filter(nums, i => i > 1);
static int Filter(List<int> nums, Expression<Func<int, bool>> expression)
{
   
    return nums.First(i => expression.Compile()(i));
}

此时,它的结构树如下:
在这里插入图片描述

四、成品代码

在DotNetCommon.Core``已封装好了表达式树简化的方法,如下:
在这里插入图片描述
更多细节,参考:《DotNetCommon源码》

相关推荐

  1. 一个简单 C# 算术表达式 Eval 解析器 MathEvalor

    2024-02-09 20:28:01       10 阅读
  2. c++ Lambda表达式 简单实验

    2024-02-09 20:28:01       9 阅读
  3. C#中Lambda表达式

    2024-02-09 20:28:01       36 阅读
  4. c#通过ExpressionTree 表达式实现对象关系映射

    2024-02-09 20:28:01       26 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-02-09 20:28:01       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-02-09 20:28:01       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-02-09 20:28:01       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-02-09 20:28:01       20 阅读

热门阅读

  1. shell脚本之无限计时器

    2024-02-09 20:28:01       29 阅读
  2. c++小游戏整理

    2024-02-09 20:28:01       25 阅读
  3. C语言——oj刷题——实现字符串逆序

    2024-02-09 20:28:01       31 阅读
  4. OpenVPN简记

    2024-02-09 20:28:01       26 阅读
  5. Python循环语句——for循环临时变量作用域

    2024-02-09 20:28:01       36 阅读