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代码,存在一个私有密封类用于存储匿名方法,由编译器负责转换实现的
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类来解析该过程
其中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,望技术有成后能回来看见自己的脚步