Lambda&Expression Tree
目录
Lambda演变历史
.Net Framework 1.0/1.1
public class EvolutionHistory
{
  public delegate void CustomDelegate(string param);
  
  public void Test()
  {
    CustomDelegate customDelegate = new CustomDelegate(DoWork);
    customDelegate.Invoke("test");
  }
  
  private void DoWork(string param)
  {
    Console.WriteLine(param);
  }
}
.Net Framework 2.0
匿名方法,delegate关键字出现,简化了委托实例化参数
public class EvolutionHistory
{
  public delegate void CustomDelegate(string param);
  
  public void Test()
  {
    CustomDelegate customDelegate = new CustomDelegate(delegate (string param)
    {
      Console.WriteLine(param);
    });
    customDelegate.Invoke("test");
  }
}
.Net Framework 3.0
Lambda表达式出现
- 去掉delegate关键字换成=>
public class EvolutionHistory
{
  public delegate void CustomDelegate(string param);
  
  public void Test()
  {
    CustomDelegate customDelegate = new CustomDelegate((string param) =>
    {
      Console.WriteLine(param);
    });
    customDelegate.Invoke("test");
  }
}
- 编译器语法糖辅助,去掉参数类型,由委托参数推断
public class EvolutionHistory
{
  public delegate void CustomDelegate(string param);
  
  public void Test()
  {
    CustomDelegate customDelegate = new CustomDelegate(param =>
    {
      Console.WriteLine(param);
    });
    customDelegate.Invoke("test");
  }
}
- 方法主体只有一行语句,可精简双括号
public class EvolutionHistory
{
  public delegate void CustomDelegate(string param);
  
  public void Test()
  {
    CustomDelegate customDelegate = new CustomDelegate(param =>
      Console.WriteLine(param)
    );
    customDelegate.Invoke("test");
  }
}
- 编译器语法糖辅助,去掉实例化委托过程
public class EvolutionHistory
{
  public delegate void CustomDelegate(string param);
  
  public void Test()
  {
    CustomDelegate customDelegate = param => Console.WriteLine(param);
    customDelegate.Invoke("test");
  }
}
Lambda表达式本质
Lambda表达式实际指定的部分,参数列表**=> 表达式或语句块;**
 查看IL代码,存在一个私有密封类用于存储匿名方法,由编译器负责转换实现的
查看IL代码,存在一个私有密封类用于存储匿名方法,由编译器负责转换实现的

Expression Tree
表达式树是什么
来回顾下数学中的二次函数表达式
 对其语义拆分后对应的树状结构
对其语义拆分后对应的树状结构
 用树状的数据结构来描述代码,便是表达式树(表达式可拆分成树状结构),提供数据存储,不用于计算。
用树状的数据结构来描述代码,便是表达式树(表达式可拆分成树状结构),提供数据存储,不用于计算。
表达式树基类
在C#中由Expression来定义,如上用表达式树构建,如下这种方式是基于Lambda表达式快速构建表达式树
Expression<Func<int,int>> expression = x=>(x+1)*(x-2)
查看下expression的信息,内部的Body中,Left和Right,不断的将表达式主体拆分细化,也就呈现了上图那样的树状结构,每一个节点都作为存储。

表达式树构建
Lambda构建表达式树
用Lambda表达式构建表达式树,构建一个Lambda表达式树。但需注意的是该种方式不能使用语句块。
Expression<Func<int,int>> expression = x=>(x+1)*(x-2);
通过ILSpy可以查看下编译后的IL代码,可以看到编译器把Lambda表达式转换成了一堆的
构建过程。
此处得区分下Lambda表达式与Lambda表达式树的概念了。
- Lambda表达式:x=>(x+1)*(x-2); 
- Lambda表达式树:用Lambda表达式声明表达式树便是如上构建的expression。 
组装构建表达式树
除了用Lambda表达式快速构建表达式树,也可以一个节点一个节点构建,将所有节点组装起来构建一个Lambda表达式树。
ParameterExpression param = Expression.Parameter(typeof(int), "x");
BinaryExpression addExpression = Expression.Add(param, Expression.Constant(1));
BinaryExpression subtractExpression = Expression.Subtract(param, Expression.Constant(2));
BinaryExpression multiplyExpression = Expression.Multiply(addExpression, subtractExpression);
Expression<Func<int, int>> expression = Expression.Lambda<Func<int, int>>(multiplyExpression, param);
通过ILSpy查看下编译后的IL,可以看到对应的构建过程。

表达式树构建方式区别
用Lambda表达式构建表达式树通过借助编译器将该段Lambda表达式转换成表达式树的形式,通过ILSpy比对用组装构建的表达式树,两种是一样的。而具体哪种方式灵活,哪种方式扩展更强,就不言而喻了。
组装表达式树中常用类型

表达式树构建过程
表达式树拼接过程是逐个节点的汇聚,依照中序遍历过程,以左中右,从底层节点从左往右构建起来。

表达式树执行
表达式树本身承担责任为存储,并不能直接执行,需要转换为委托实例执行
Func<int, int> func = expression.Compile();
func.Invoke(1);
Expression Visitor
解析表达式树是逆过程,反向拆分每一个节点,借助前序遍历,以中左右,从顶部中间节点往下遍历解析。
 Expression中提供了ExpressionVisitor类来解析该过程
Expression中提供了ExpressionVisitor类来解析该过程
 其中Visit方法主要职责是根据node类型,将其分配给具体的该节点类型处理方法,如同Switch般,将这个表达式分配到符合node类型的方法内。
其中Visit方法主要职责是根据node类型,将其分配给具体的该节点类型处理方法,如同Switch般,将这个表达式分配到符合node类型的方法内。
简单解析表达式树转Sql
- 先借助Lambda表达式构建表达式树
Expression<Func<OrderItem, bool>> lambdaExpression = x =>
        x.Quantity > 5 && x.Quantity < 10 &&
        x.Name.StartsWith("a") &&
        x.Name.EndsWith("b") &&
        x.Name.Contains("c");
- 创建一个ConditionBuilderVisitor类并继承ExpressionVisitor,其中对几种类型进行方法
public class ConditionBuilderVisitor : ExpressionVisitor
{
    private readonly Stack<string> _stringStack = new Stack<string>();
    public ConditionBuilderVisitor(Expression expression)
    {
        Visit(expression);
        Condition();
    }
    public void Condition()
    {
        string condition = string.Concat(_stringStack.ToArray());
        _stringStack.Clear();
        Console.WriteLine(condition);
    }
    public override Expression Visit(Expression node)
    {
        Console.WriteLine($"Visit:{node.ToString()}");
        return base.Visit(node);
    }
}
- 对二元表达式节点解析,将其存入栈内,对节点连接关系转换成Sql语言。
protected override Expression VisitBinary(BinaryExpression node)
{
    if (node == null) throw new ArgumentNullException("BinaryExpression");
    _stringStack.Push(")");
    base.Visit(node.Right);
    _stringStack.Push(" " + ToSqlOperator(node.NodeType) + " ");
    base.Visit(node.Left);
    _stringStack.Push("(");
    return node;
}
private string ToSqlOperator(ExpressionType type)
{
    switch (type)
    {
        case (ExpressionType.AndAlso):
        case (ExpressionType.And):
            return "AND";
        case (ExpressionType.OrElse):
        case (ExpressionType.Or):
            return "OR";
        case (ExpressionType.Not):
            return "NOT";
        case (ExpressionType.NotEqual):
            return "<>";
        case ExpressionType.GreaterThan:
            return ">";
        case ExpressionType.GreaterThanOrEqual:
            return ">=";
        case ExpressionType.LessThan:
            return "<";
        case ExpressionType.LessThanOrEqual:
            return "<=";
        case (ExpressionType.Equal):
            return "=";
        default:
            throw new Exception("不支持该方法");
    }
}
- 对为类中属性的节点解析,存入到栈中
protected override Expression VisitMember(MemberExpression node)
{
    if (node == null) throw new ArgumentNullException("MemberExpression");
    _stringStack.Push(" [" + node.Member.Name + "] ");
    return node;
}
- 对为方法的节点解析,存入到栈中
protected override Expression VisitMethodCall(MethodCallExpression m)
{
    if (m == null) throw new ArgumentNullException("MethodCallExpression");
    string format;
    switch (m.Method.Name)
    {
        case "StartsWith":
            format = "({0} LIKE {1}+'%')";
            break;
        case "Contains":
            format = "({0} LIKE '%'+{1}+'%')";
            break;
        case "EndsWith":
            format = "({0} LIKE '%'+{1})";
            break;
        default:
            throw new NotSupportedException(m.NodeType + " is not supported!");
    }
    Visit(m.Object);
    Visit(m.Arguments[0]);
    string right = _stringStack.Pop();
    string left = _stringStack.Pop();
    _stringStack.Push(string.Format(format, left, right));
    return m;
}
- 对为常量的的节点解析,存入栈中
protected override Expression VisitConstant(ConstantExpression node)
{
    if (node == null) throw new ArgumentNullException("ConstantExpression");
    _stringStack.Push(" '" + node.Value + "' ");
    return node;
}
- 用Visitor去解析表达式树
ConditionBuilderVisitor vistor = new ConditionBuilderVisitor(lambdaExpression);
- 这样一来,可以将表达式树转Sql输出了 
2021-02-27,望技术有成后能回来看见自己的脚步。