表达式树简要学习

概要简述

表达式树以树形数据结构表示代码,其中每一个节点都是一种表达式,比如方法调用和 x < y 这样的二元运算等

每一个表达式都有节点类型NodeType
例如:二元操作、lambda、对象访问登都可以通过一个数据结构进行表示

Expression

Expression是一个抽象类,不可进行实例化,但是可以通过Expression提供的静态方法进行对应类型的表达式创建,例如Expression.Parameter()Expression.Constant()等,还可以将多个表达式合并,例如Expression.MakeBinary()Expression.MakeMemberAccess
基本上只要你写的代码都能用表达式进行表示,包括trycatch,表达式还能替代反射进行对象的创建(大量创建对象效率比反射高,不仅创建效率高于反射,消耗内存也比反射小)拼接sql(使用Visitor)

二元操作

二元操作是指操作符两边都有操作数,常见的二元操作符号有:+-*/%和逻辑运算等
二元操作符都可以划分为leftright,其实是一个二叉树的结构
例如:(3 * 5 + 2 * 5) * 9 + x * 2

	 ConstantExpression c3 = Expression.Constant(3);
	 ConstantExpression c5 = Expression.Constant(5);
	 ConstantExpression c2 = Expression.Constant(2);
	 ConstantExpression c6 = Expression.Constant(9);
	 ParameterExpression cx = Expression.Variable(typeof(int), "x");
	 BinaryExpression c3xc5 = Expression.MakeBinary(ExpressionType.Multiply, c3, c5); // 3 * 5
	 BinaryExpression c2xc5 = Expression.MakeBinary(ExpressionType.Multiply, c2, c5); // 2 * 5
	 BinaryExpression p1plusp2 = Expression.MakeBinary(ExpressionType.Add, c3xc5, c2xc5); // 3 * 5 + 2 * 5
	 BinaryExpression p1plusp2x6 = Expression.MakeBinary(ExpressionType.Multiply, p1plusp2, c6); // (3 * 5 + 2 * 5) * 9
	 BinaryExpression cxxc2 = Expression.MakeBinary(ExpressionType.Multiply, cx, c2); // x * 2
	 BinaryExpression exp = Expression.MakeBinary(ExpressionType.Multiply, p1plusp2x6, cxxc2); // (3 * 5 + 2 * 5) * 9 + x * 2

运行结果如下:
在这里插入图片描述

lambda

表达式允许的lambda仅能运行一行代码,多由箭头函数进行表示,在lambda表达式中分成两部分:parameterbody,前者为参数,后者为方法体,举个例子:(int c, int d, int e) => c * d + d * e + 2 * d

	ParameterExpression pc = Expression.Parameter(typeof(int), "c");
	ParameterExpression pd = Expression.Parameter(typeof(int), "d");
	ParameterExpression pe = Expression.Parameter(typeof(int), "e");
	BinaryExpression pcxpd = Expression.MakeBinary(ExpressionType.Multiply, pc, pd); // c * d
	BinaryExpression pdxpe = Expression.MakeBinary(ExpressionType.Multiply, pd, pe); // d * e
	BinaryExpression pdx2 = Expression.MakeBinary(ExpressionType.Multiply, pd, Expression.Constant(2)); // d * 2
	BinaryExpression body = Expression.MakeBinary(ExpressionType.Add, Expression.MakeBinary(ExpressionType.Add, pcxpd, pdxpe), pdx2); // c * d + d * e + d * 2
	Expression<Func<int, int, int, int>> c = Expression.Lambda<Func<int, int, int, int>>(body, pc, pd, pe); // (c, d, e) => c * d + d * e + d * 2

执行结果如下:
在这里插入图片描述

访问对象属性

表达式允许访问某个对象实例的属性,利用反射可以很好的进行对象的一些操作,甚至能不经反射直接创建对象实例

简单的访问对象属性
	Student student = new Student()
	{
	    Id = 1,
	    Name = "ainuo5213",
	};
	Type type = student.GetType();
	PropertyInfo prop = type.GetProperty("Id");
	ParameterExpression parameter = Expression.Parameter(type, "student");
	MemberExpression exp = Expression.MakeMemberAccess(parameter, prop);

运行结果如下
在这里插入图片描述

利用表达式创建对象

利用表达式创建对象,只需要使用Expression.New,如果需要初始化某些值的话使用Expression.MemberInit

	Type stuType = typeof(Student);
	List<MemberAssignment> memberAssigns = new List<MemberAssignment>();
	PropertyInfo targetProp = stuType.GetProperty("Id", BindingFlags.Public | BindingFlags.Instance); // 获取属性信息
	ConstantExpression c2 = Expression.Constant(2);
	var newObjExp = Expression.New(stuType); // new Student()
	MemberAssignment memberBinding = Expression.Bind(targetProp, c2); // Id = 2,绑定目标属性赋值
	memberAssigns.Add(memberBinding);
	var obj = Expression.MemberInit(newObjExp, memberAssigns); // new Student(){ Id = 2 }

执行结果如下:
在这里插入图片描述

利用lambda创建真的对象

前面的知识已经有lambda表达式的expression使用方法和对象创建的表达式方法,这里将两者结合起来,进行创建内存中的对象

	 ParameterExpression idExp = Expression.Parameter(typeof(int), "Id"); // 参数Id
	 ParameterExpression nameExp = Expression.Parameter(typeof(string), "Name"); // 参数Name
	 Type stuType = typeof(Student);
	 List<MemberAssignment> memberAssigns = new List<MemberAssignment>(); // 属性声明列表
	 PropertyInfo idPro = stuType.GetProperty("Id", BindingFlags.Public | BindingFlags.Instance); // 获取
	 Id属性
	 PropertyInfo nameProp = stuType.GetProperty("Name", BindingFlags.Public | BindingFlags.Instance); // 获取Name属性
	 var newObjExp = Expression.New(stuType);
	 MemberAssignment idBinding = Expression.Bind(idPro, idExp); // 绑定Id
	 MemberAssignment nameBinding = Expression.Bind(nameProp, nameExp); // 绑定Name
	 memberAssigns.Add(idBinding);
	 memberAssigns.Add(nameBinding);
	 var body = Expression.MemberInit(newObjExp, memberAssigns); // 初始化
	 Expression<Func<int, string, Student>> c = Expression.Lambda<Func<int, string, Student>>(body, idExp, nameExp);
	 Student st = c.Compile()(1, "111");
	 Console.WriteLine(c.ToString());
	 Console.WriteLine(st.Id);
	 Console.WriteLine(st.Name);

执行结果如下:
在这里插入图片描述

小工具:DTO转换

public static TTarget Map(TSource source)
{
     if (source == null)
     {
         return null;
     }

     Type sourceType = typeof(TSource);
     Type targetType = typeof(TTarget);
     Expression parameterExpression = Expression.Parameter(sourceType, "p");
     List<MemberBinding> memberBindingList = new List<MemberBinding>();
     foreach (PropertyInfo sourceProp in sourceType.GetProperties())
     {
         PropertyInfo targetProp = targetType.GetProperty(sourceProp.Name, BindingFlags.Public | BindingFlags.Instance);
         if (targetProp == null || sourceProp.PropertyType != targetProp.PropertyType || !targetProp.CanWrite)
         {
             continue;
         }

         MemberExpression property = Expression.Property(parameterExpression, sourceProp); // p.xxx
         MemberAssignment memberBinding = Expression.Bind(targetProp, property); // xxx = p.xxx
         memberBindingList.Add(memberBinding);
     }

     MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(targetType), memberBindingList);

     Func<TSource, TTarget> func = Expression.Lambda<Func<TSource, TTarget>>(memberInitExpression, parameterExpression as ParameterExpression).Compile();
     return func(source);
 }

ExpressionVisitor

ExpressionVisitorExpression一样也是个抽象类,不能直接用new,需要继承进行对应逻辑的处理。
ExpressionVisitor采用访问者模式Expression的每一个节点进行访问,开发者可通过重写基类方法进行对应逻辑的处理,例如简单的拼接where条件的sql

public class ConditionExpressionVisitor : ExpressionVisitor
{
    private Stack<string> _sqlStack = new Stack<string>();

    public ConditionExpressionVisitor()
    {
        this._sqlStack.Clear();
    }

    [return: NotNullIfNotNull("node")]
    protected override Expression VisitBinary(BinaryExpression node)
    {
        this._sqlStack.Push(")");
        base.Visit(node.Right);
        this._sqlStack.Push(" " + ExpressionUtil.ConditionOperator(node.NodeType) + " ");
        base.Visit(node.Left);
        this._sqlStack.Push("(");

        return node;
    }

    public string GetSql()
    {
        return string.Concat(this._sqlStack.ToArray());
    }

    protected override Expression VisitMember(MemberExpression node)
    {
        base.Visit(node.Expression);
        this._sqlStack.Push(" [" + node.Member.Name + "] ");

        return node;
    }

    protected override Expression VisitConstant(ConstantExpression node)
    {
        string op = string.Empty;
        if (!this.IsNumber(node.Type))
        {
            op = "'";
        }
        this._sqlStack.Push($" {op}" + node.Value + $"{op}");
        return base.VisitConstant(node);
    }

    private bool IsNumber(Type type)
    {
        return type == typeof(decimal) || type == typeof(double) || type == typeof(int) || type == typeof(float) || type == typeof(Enum);
    }

    protected override Expression VisitMethodCall(MethodCallExpression node)
    {
        string _format = string.Empty;
        switch (node.Method.Name)
        {
            case "StartsWith":
                _format = "{0} LIKE {1}+'%'";
                break;
            case "EndsWith":
                _format = "{0} LIKE '%'+{1}";
                break;
            case "Contains":
                _format = "{0} LIKE '%'+{1}+'%'";
                break;
            default:
                throw new NotSupportedException(node.NodeType + " is not supported!");
        }
        base.Visit(node.Object);
        base.Visit(node.Arguments[0]);
        string right = this._sqlStack.Pop();
        string left = this._sqlStack.Pop();
        this._sqlStack.Push(string.Format(_format, left, right));
        return node;
    }
}

执行结果如下:

ConditionExpressionVisitor operatorExpressionVisitor = new ConditionExpressionVisitor();
Expression<Func<Student, bool>> exp = n => n.Id > 50 && n.Name.StartsWith("张三") && n.Name == "111";
operatorExpressionVisitor.Visit(exp);
string sql = operatorExpressionVisitor.GetSql();

在这里插入图片描述

总的来说,expression可以表示你所写的任意代码,包括但不仅仅只有笔者所写的这几类,能做的事情很多(微软官方对于这部分描述较少,且没有例子),笔者代码仅供参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值