(1)Expression我们称为是表达式树,是一种数据结构体,用于存储需要计算,运算的一种结构,这种结构可以只是存储,而不进行运算。通常表达式目录树是配合Lambda一起来使用的,lambda可以是匿名方法,当然也可以使用Expression来动态的创建!
1、Func是方法
Func
func = (m, n) => m * n + 2; Console.WriteLine(func.Invoke(1, 1)); //运算:1*1+2=3
2、Expression是数据结构
//lambda表达式声明表达式目录树 Expression
> exp = (m, n) => m * n + 2; int result = exp.Compile().Invoke(1, 2); Console.WriteLine(result); //运算:1*2+2=4 注意:Expression只能为1行(如下图会报错)
3、使用ILSpy反编译解析看一下
调一下格式更好看一点
ParameterExpression parameterExpression = Expression.Parameter(typeof(int), "m"); ParameterExpression parameterExpression2 = Expression.Parameter(typeof(int), "n"); var multiply = Expression.Multiply(parameterExpression, parameterExpression2); var constant = Expression.Constant(2,typeof(int)); var add = Expression.Add(multiply, constant); Expression
> exp = Expression.Lambda >( add, new ParameterExpression[2] { parameterExpression, parameterExpression2 }); 打印看看结果
int result = exp.Compile().Invoke(11, 12); Console.WriteLine(result);
得到134,与m*n+2得出结果一致
4、拼装练习
(1)练习一:
(2)练习二
(3)练习三
5、动态生成硬编码(通用、性能好)
需求:我希望复制一个People出来
public class People { public int Age; public string Name; } public class PeopleCopy { public int Age; public string Name; }
方法1:通过硬编码直接赋值
People people = new People() { Age = 18, Name = "吴彦祖" }; PeopleCopy peopleCopy = new PeopleCopy() { Age = people.Age, Name = people.Name, };
方法2:通过反射赋予
方法3:通过Json序列化与反序列化赋值
第一种方法性能最好,但是不够通用。方法2和方法3性能不好。
方法4:
这时候可以考虑使用表达式目录树来动态生成硬编码
思路:用表达目录树动态生成硬编码,生成保存到字典里,下次再调用的时候则直接从字典里拿。
public class ExpressionMapper { private static Dictionary
_dic = new Dictionary (); public static TOut Trans (TIn tIn) { string key = string.Format("funckey_{0}_{1}", typeof(TIn).FullName, typeof(TOut).FullName); if (!_dic.ContainsKey(key)) { ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p"); List memberBindingList = new List (); foreach (var item in typeof(TOut).GetProperties()) { MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name)); MemberBinding memberBinding = Expression.Bind(item, property); memberBindingList.Add(memberBinding); } foreach (var item in typeof(TOut).GetFields()) { MemberExpression field = Expression.Field(parameterExpression, typeof(TIn).GetField(item.Name)); MemberBinding memberBinding = Expression.Bind(item, field); memberBindingList.Add(memberBinding); } MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray()); Expression > lambda = Expression.Lambda >(memberInitExpression, new ParameterExpression[] { parameterExpression }); _dic[key] = lambda.Compile(); } return ((Func )_dic[key]).Invoke(tIn); } 方法5:泛型缓存(相比方法4可以节省读取字典时候的消耗)
public class ExpressionGenericMapper
{ private static Func _Func; static ExpressionGenericMapper() { ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p"); List memberBindingList = new List (); foreach (var item in typeof(TOut).GetProperties()) { MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name)); MemberBinding memberBinding = Expression.Bind(item, property); memberBindingList.Add(memberBinding); } foreach (var item in typeof(TOut).GetFields()) { MemberExpression field = Expression.Field(parameterExpression, typeof(TIn).GetField(item.Name)); MemberBinding memberBinding = Expression.Bind(item, field); memberBindingList.Add(memberBinding); } MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray()); Expression > lambda = Expression.Lambda >(memberInitExpression, new ParameterExpression[] { parameterExpression }); _Func = lambda.Compile(); } public static TOut Trans(TIn t) { return _Func(t); } 看一下字典缓存和泛型类缓存消耗的时间,明显可以看到通过泛型类缓存的性能更好,因为节省了查找字典的性能消耗。
PrintExecutionTime(() => { Console.WriteLine("通过字典缓存,第一次消耗时间:"); PeopleCopy copy = ExpressionMapper.Trans
(new People() { Age = 10, Name = "哇哈哈" }); }); PrintExecutionTime(() => { Console.WriteLine("通过字典缓存,第二次消耗时间:"); PeopleCopy copy2 = ExpressionMapper.Trans (new People() { Age = 10, Name = "哇哈哈" }); }); PrintExecutionTime(() => { Console.WriteLine("通过泛型类缓存,第一次消耗时间:"); PeopleCopy copy3 = ExpressionGenericMapper .Trans(new People() { Age = 11, Name = "啦啦啦" }); }); PrintExecutionTime(() => { Console.WriteLine("通过泛型类缓存,第二次消耗时间:"); PeopleCopy copy4 = ExpressionGenericMapper .Trans(new People() { Age = 11, Name = "啦啦啦" }); });
5、表达式目录树动态生成的用途:
可以用来替代反射,因为反射可以通用,但是性能不够
可以生成硬编码,可以提升性能
6、递归解析表达式目录树
(1)ExpressionVisitor:肯定得递归解析表达式目录树,因为不知道深度的一棵树
(2)只有一个入口叫Visit
(3)首先检查是个什么类型的表达式,然后调用对应的protected virtual
(4)得到结果继续去检查类型——调用对应的Visit方法——再检测——再调用。。。
案例:解析(m*n+2)
第二步:检测到二元表达式,m*n+2进入VisitBinary方法.
node.Left为m*n(这里会再次调用VisitBinary方法) node.Right为2
第三步:m*n也时二元表达式,因此重新进入VisitBinary方法
node.Left为m(进入VisitParameter方法) node.Right为n(进入VisitParameter方法)
第四步:m*n解析完开始解析2,会进入VisitConstant方法
从下图可以发现,表达式expression变成了m*n-2
7、表达式拼接
using System.Linq.Expressions; namespace ConsoleApp1 { public static class ExpressionExtend { public static Expression
> And (this Expression > expr1, Expression > expr2) { //return Expression.Lambda >(Expression.AndAlso(expr1.Body, expr2.Body)); ParameterExpression newParameter = Expression.Parameter(typeof(T), "c"); NewExpressionVisitor visitor = new NewExpressionVisitor(newParameter) ; var left = visitor.Replace(expr1.Body); var right = visitor.Replace(expr2.Body); var body = Expression.And(left, right); return Expression.Lambda >(body, newParameter); } public static Expression > Or > expr1, Expression > expr2) { ParameterExpression newParameter = Expression.Parameter(typeof(T), "c"); NewExpressionVisitor visitor = new NewExpressionVisitor(newParameter); var left = visitor.Replace(expr1.Body); var right = visitor.Replace(expr2.Body); var body = Expression.Or(left, right); return Expression.Lambda >(body, newParameter); } public static Expression > Not (this Expression > expr) { var candidateExpr = expr.Parameters[0]; var body = Expression.Not(expr.Body); return Expression.Lambda >(body, candidateExpr); } } public class NewExpressionVisitor : ExpressionVisitor { public ParameterExpression _NewParameter { get; private set; } public NewExpressionVisitor(ParameterExpression param) { this._NewParameter = param; } public Expression Replace(Expression exp) { return this.Visit(exp); } protected override Expression VisitParameter(ParameterExpression node) { return this._NewParameter; } } } public static void Show() { Expression
> lambdal = x => x.Age > 5; Expression > lambda2 = x => x.ID > 5; Expression > lambda3 = lambdal.And(lambda2); Expression > lambda4 = lambdal.Or(lambda2); Expression > lambda5 = lambdal.Not(); var peopleList = Do(lambda3); for (int i = 0; i < peopleList.Count; i++) { Console.WriteLine(peopleList[i].Name); } } private static List Do(Expression > func) { List people = new List () { new People() { ID = 1, Age = 10, Name = "老一" }, new People() { ID = 2, Age = 20, Name = "老二" }, new People() { ID = 3, Age = 60, Name = "老三" }, new People() { ID = 4, Age = 18, Name = "老四" }, new People() { ID = 5, Age = 10, Name = "哈哈哈" }, new People() { ID = 6, Age = 15, Name = "方式" }, new People() { ID = 7, Age = 10, Name = "老六啦啦啦啦啦啦啦" }, }; return people.Where(func.Compile()).ToList(); } 调试成功,ID>5且age>5的只有这两个人。
表达式树也可以通过这种办法进行查询操作