05.表达式目录树Expression

参考文章

1. 基本了解

1.1 Lambda表达式

演变过程

using System;

namespace lq1
{
    class Program
    {
        public delegate void Tesk(int x);
        public delegate int TeskPara(int x);
        static void Main(string[] args)
        {
            new Program().Run();
        }

        public void Show(int x)
        {
            Console.WriteLine("Show");
        }
        public void Run()
        {
            // Lambda演变历史
            {
                // .net framework 1.0/1.1
                Tesk tesk = new Tesk(this.Show);
                tesk.Invoke(1);
            }
            int i = 0;
            {
                // .net framework 2.0,匿名方法,增加delegate关键字,可以访问局部变量
                Tesk tesk = new Tesk(delegate (int x)
                {
                    Console.WriteLine("Show" + i);
                });
                tesk.Invoke(2);
            }
            {
                // .net framework 3.0 移除delegate关键字,增加 => 语法(goes to)
                Tesk tesk = new Tesk((int x) =>
                {
                    Console.WriteLine("Show" + i);
                });
                tesk.Invoke(3);
            }
            {
                // 可以省略参数类型,参数类型根据委托自动推断
                Tesk tesk = new Tesk((x) =>
                {
                    Console.WriteLine("Show" + i);
                });
                tesk.Invoke(3);
            }
            {
                // 只有一个参数或一行代码时省略括号
                Tesk tesk = new Tesk(x => Console.WriteLine("Show" + i));
                tesk.Invoke(3);
            }
            {
                // 省略实例委托代码
                Tesk tesk = x => Console.WriteLine("Show" + i);
                tesk.Invoke(3);
            }
            {
                // 有返回值且有一行代码时,可以直接写返回值(省略 return)
                TeskPara tesk = x => x + 1;
                tesk.Invoke(5);

                Func<int, int> func = x => x + 2;
                func.Invoke(5);
            }
        }
    }
}

概述说明

Lambda表达式是一个特殊的匿名函数,是一种高效的类似于函数式编程的表达式,简化开发中需要编写的代码量

可以包含表达式和语句,并且可用于创建委托或表达式目录树类型,支持带有可绑定到委托或表达式树的输入参数的内联表达式

所有Lambda表达式都使用Lambda运算符=>,运算符的左边是输入参数(如果有),右边是表达式或语句块

Lambda表达式不能在委托链中进行删(-=)操作,因为每个表达式的名称都不一样

1.2 隐式类型

在C# 3.0中,引进了关键字叫做varvar允许声明一个新变量,它的类型是从用来初始化器变量的表达式里隐式的推断出来的,即在声明时,不需要给它定义类型,它会根据它的初始化器表达式来推断出它的类型

var 本身不是类型,而是向编译器发出一条用来推断和分配类型的指令,因此,隐式类型也叫推断类型,由编辑器自动根据表达式推断出对象的最终类型

隐式类型的本地变量是强类型变量(就好像您已经声明该类型一样),但由编译器确定类型

var Name = "李白";
var Age = 18;
var Interest = new List<string>{"唱歌","阅读"}

1.3 匿名类型

概述说明

字面意思:没有名字的类型

演变历史

public void Starting()
{
    {
        // 使用 object 声明
        object model = new
        {
            id = 1,
            name = "libai"
        };
        //Console.WriteLine(model.id);  强类型语言,编译器检测不到object类型中有id属性
    }
    {
        // 使用 dynamic 声明,动态类型,避开编译器检查
        dynamic model = new
        {
            id = 2,
            name = "libai"
        };
        Console.WriteLine(model.id);
        Console.WriteLine(model.age);   // 运行时出错
    }
    {
        // 使用 var 声明,匿名类型,自动推断,声明后的属性是只读的
        var model = new
        {
            id = 3,
            name = "libai"
        };
        Console.WriteLine(model.id);
        //Console.WriteLine(model.age); 编译器检测(自动推断)无age字段
    }
}

1.4 扩展方法

概述说明

扩展方法:允许在不修改类型的内部代码的情况下为类型添加独立的行为

扩展方法只能定义在 非泛型的静态类中,使用 static修饰,参数使用this关键字 修饰要扩展的类。就是说扩展方法的第一个参数必须是this关键开头然后经跟要扩展的对象类型,然后是扩展对象在运行时的实例对象引用

扩展方法是一种特殊的静态方法,可以像扩展类型上的实例方法一样进行调用,能向现有类型“添加”方法,而无须创建新的派生类型、重新编译或以其他方式修改原始类型

在使用时编译器认为一个表达式要使用一个实例方法,但是没有找到,需要检查导入的命名空间和当前命名空间里所有的扩展方法,并匹配到适合的方法

示例一:简单定义

public static class Extend
{
    public static int ToInt(this int? k)
    {
        return k ?? 0;
    }
}

示例二:简单定义

public interface ILog
{
    void log(string message,LogLevel logLevel);
}

public static class ILogExtensions
{
    //记录调试信息
    public static void LogDebug(this ILog logger,string message)
    {
        if(true) //判断日志配置中是否允许输入Debug类型的日志
        {
            logger?.Log($"{message}",LogLevel.Debug);
        }
    }
}

示例三:模拟 where 方法

using System;
using System.Collections.Generic;

namespace lq2
{
    class Program
    {
        static void Main(string[] args)
        {
            List<User> list = new List<User>
            {
                new User(){uid=1,uname="a1",age=18,gender=0 },
                new User(){uid=2,uname="a2",age=28,gender=1 },
                new User(){uid=3,uname="a3",age=23,gender=1 },
                new User(){uid=4,uname="a4",age=18,gender=0 },
                new User(){uid=5,uname="a5",age=33,gender=1 }
            };

            var d1 = list.MyWhere(x => x.uid > 3);
            Console.WriteLine(d1.Count);

            var d2 = list.MyWhere(x => x.age >= 18 && x.gender == 0);
            Console.WriteLine(d2.Count);
        }
    }

    public class User
    {
        public int uid { get; set; }
        public int age { get; set; }
        public string uname { get; set; }
        public int gender { get; set; }
    }

    public static class Extend
    {
        public static List<T> MyWhere<T>(this List<T> rouse, Func<T, bool> func)
        {
            List<T> list = new List<T>();

            foreach (var item in rouse)
            {
                if (func(item))
                {
                    list.Add(item);
                }
            }
            return list;
        }
    }
}

2. 表达式目录树Expression

2.1 简单概述

表达式树,ExpressionSystem.Linq.Expressions),是一种数据结构体,用于存储需要计算,运算的一种结构,这种结构可以只是存储,而不进行运算,或者说是描述不同变量和常用之间关系的一种数据结构

表达式目录树以数据形式表示语言级别代码,数据存储在树形结构中,目录树中的每个节点都表示一个表达式,简单的说是一种语法树,或者说是一种数据结构

表达式目录树不能有语句体,不能当作方法,不能有大括号,只能有一行代码

2.2 声明表达式目录树

第一种方式,快捷声明,用Lambda声明表达式目录树

示例一:普通类型

Expression<Func<int, int, int>> exp = (n, m) => n * m + 2;

示例二:实体类声明

Expression<Func<User, bool>> lambda = x => x.age > 18;

第二种方式,手动拼装目录树(原始方式),简单示例

namespace e1
{
    using System;
    using System.Linq.Expressions;
    class Program
    {
        static void Main(string[] args)
        {
            // 以此表达式为例,手动拼接,实现相同作用
            Expression<Func<int, int, int>> func = (x, y) => x * y + 2;

            // 声明变量表达式
            ParameterExpression px = Expression.Parameter(typeof(int), "x");
            ParameterExpression py = Expression.Parameter(typeof(int), "y");

            // 声明常量表达式
            ConstantExpression constant = Expression.Constant(2, typeof(int));

            // 声明乘积表达式
            BinaryExpression multiply = Expression.Multiply(px, py);

            // 声明相加表达式
            BinaryExpression add = Expression.Add(multiply, constant);

            // 声明参数表达式
            ParameterExpression[] parameters = new ParameterExpression[] { px, py };

            // 生成表达式目录树
            Expression<Func<int, int, int>> exp = Expression.Lambda<Func<int, int, int>>(add, parameters);

            // 表达式目录树生成委托
            var ifunc = exp.Compile();

            Console.WriteLine("委托结果:" + func.Compile().Invoke(1, 2));
            Console.WriteLine("表达式树:" + ifunc.Invoke(1, 2));
        }
    }
}

2.3 表达式类型

方法 类型 描述
Expression.Parameter(...) ParameterExpression 表示一个命名参数(变量)表达式
Expression.Constant(...) ConstantExpression 表示具有常量值的表达式
Expression.Add(...) BinaryExpression 表示具有(+,-,*,/)运算的表达式
Expression.Property/Field(...) MemberExpression 表示访问属性或字段
Expression.Call(...) MethodCallExpression 表示对静态方法或实例方法的调用
Expression.Condition(...) ConditionalExpression 表示包含条件运算符的表达式
LambdaExpression 描述一个Lambda表达式
ListInitExpression 表示包含集合初始值设定项的构造函数调用
NewExpression 表示构造函数调用
NewArrayExpression 表示创建新数组并可能初始化改数组的元素
MemberMemberBinding 表示初始化新创建对象的成员的成员
MemberInitExpression 表示调用构造函数并初始化新对象的一个或多个成员
MemberAssignment 表示初始化新创建对象的字段或属性
InvocationExpression 表示将委托或Lambda表达式应用于参数表达式列表的表达式
TypeBinaryExpression 表示表达式和类型之间的操作
UnaryExpression 表示包含一元运算符的表达式

3. 拼装Expression

3.1 变量,常量拼装

示例一:常量

static void Test1()
{
    // lambda方式
    Expression<Func<int>> func = () => 1 + 2;

    // 声明常量表达式
    ConstantExpression constant1 =  Expression.Constant(1, typeof(int));
    ConstantExpression constant2 =  Expression.Constant(2, typeof(int));

    // 声明相加表达式
    BinaryExpression add = Expression.Add(constant1, constant2);

    // 生成表达式目录树
    Expression<Func<int>> exp = Expression.Lambda<Func<int>>(add);

    // 表达式目录树生成委托
    var ifunc = exp.Compile();

    Console.WriteLine(func.Compile().Invoke());
    Console.WriteLine(ifunc.Invoke());
}

示例二:常量+变量(2.2示例)

namespace e1
{
    using System;
    using System.Linq.Expressions;
    class Program
    {
        static void Main(string[] args)
        {
            // 以此表达式为例,手动拼接,实现相同作用
            Expression<Func<int, int, int>> func = (x, y) => x * y + 2;

            // 声明变量表达式
            ParameterExpression px = Expression.Parameter(typeof(int), "x");
            ParameterExpression py = Expression.Parameter(typeof(int), "y");

            // 声明常量表达式
            ConstantExpression constant = Expression.Constant(2, typeof(int));

            // 声明乘积表达式
            BinaryExpression multiply = Expression.Multiply(px, py);

            // 声明相加表达式
            BinaryExpression add = Expression.Add(multiply, constant);

            // 声明参数表达式
            ParameterExpression[] parameters = new ParameterExpression[] { px, py };

            // 生成表达式目录树
            Expression<Func<int, int, int>> exp = Expression.Lambda<Func<int, int, int>>(add, parameters);

            // 表达式目录树生成委托
            var ifunc = exp.Compile();

            Console.WriteLine("委托结果:" + func.Compile().Invoke(1, 2));
            Console.WriteLine("表达式树:" + ifunc.Invoke(1, 2));
        }
    }
}

3.2 变量,常量,方法拼接

示例一:特殊类型示例

namespace e1
{
    using System;
    using System.Linq.Expressions;
    using System.Reflection;

    class Program
    {
        static void Main(string[] args)
        {
            Test2();
        }

        static void Test2()
        {
            // lambda方式
            Expression<Func<User, bool>> func = (u) => u.uid.ToString().Equals("1");

            // 声明变量表达式
            ParameterExpression x = Expression.Parameter(typeof(User), "x");

            // 获取字段
            PropertyInfo property = typeof(User).GetProperty("uid");

            // 获取方法
            MethodInfo toString = typeof(int).GetMethod("ToString", new Type[] { });
            MethodInfo equals = typeof(string).GetMethod("Equals", new Type[] { typeof(string) });

            // 设置常量表达式
            ConstantExpression constant = Expression.Constant("1");

            // 访问字段表达式
            MemberExpression propertyExp = Expression.Property(x, property);

            // 调用方法表达式
            var tostringExp = Expression.Call(propertyExp, toString, new Expression[0]);
            var equalsExp = Expression.Call(tostringExp, equals, new Expression[] { constant });

            // 生成表达式树
            Expression<Func<User, bool>> expression = 
                Expression.Lambda<Func<User, bool>>(equalsExp, new ParameterExpression[] { x });

            User user = new User { uid = 5 };

            Console.WriteLine(func.Compile().Invoke(user));
            Console.WriteLine(expression.Compile().Invoke(user));
        }
    }

    public class User
    {
        public int uid { get; set; }
    }
}

4. 解析Expression

使用 ExpressionVisitor 解析表达式目录树,ExpressionVisitor 表示表达式树的访问者和重写者

解析流程

  • 通过ExpressionVisitor 这个访问者类
  • 调用 Visit 入口(开始)方法解析表达式(自动根据表达式类型执行相应类型的解析方法)
  • Lambda 会区分参数和方法体,调度(自动)到更加专业的方法中解析(需要在次调用入口 Visit方法)
  • 根据表达式的类型,调度(自动)到更加专业的方法中解析(需要在次调用入口 Visit方法)
  • 根据旧的模式(表达式)产生一个新的表达式(如果需要重写的话)
  • 说明:解析顺序是从右往左解析(如果是二元表达式)

4.1 简单解析

示例一:常量

using System;
using System.Linq.Expressions;

namespace e2
{
    class Program
    {
        static void Main(string[] args)
        {
            Test1();
        }

        static void Test1()
        {
            Expression<Func<int>> expression = () => 1;

            CustomVisitor visitor = new CustomVisitor();
            var exp = visitor.Modify(expression);

        }
    }
    public class CustomVisitor : ExpressionVisitor
    {
        public Expression Modify(Expression expression)
        {
            return this.Visit(expression);
        }
        
        protected override Expression VisitConstant(ConstantExpression node)
        {
            Console.WriteLine("VisitConstant");
            return base.VisitConstant(node);
        }
    }
}

示例二:变量+常量,算术运算(二元运算类型【两个数操作】)

using System;
using System.Linq.Expressions;

namespace e2
{
    class Program
    {
        static void Main(string[] args)
        {
            Test1();
        }

        static void Test1()
        {
            // 1.创建表达式树
            Expression<Func<int, int>> expression = (y) => y + 2;
			
            // 2.创建访问类实例(使用继承是为了演示过程)
            CustomVisitor visitor = new CustomVisitor();
            // 3.调用入口方法,调用入口方法后就会自动进行默认解析(如果没有定义解析过程的话)
            var exp = visitor.Modify(expression);
        }
    }
    
    // 继承访问类,演示过程
    public class CustomVisitor : ExpressionVisitor
    {
        // 调用入口方法,开始解析,自动调用表达式类型对应的解析方法
        public Expression Modify(Expression expression)
        {
            return this.Visit(expression);
        }
		
        // 4.调用二元表达式解析方法
        protected override Expression VisitBinary(BinaryExpression node)
        {
            Console.WriteLine("VisitBinary");
            // 4.1 判断表达式操作类型
            if (node.NodeType == ExpressionType.Add)
            {
                Expression left = this.Visit(node.Left);
                Expression right = this.Visit(node.Right);

                return Expression.Subtract(left, right);
            }
            return base.VisitBinary(node);
        }
		// 4.调用常量表达式解析方法
        protected override Expression VisitConstant(ConstantExpression node)
        {
            Console.WriteLine("VisitConstant");
            return base.VisitConstant(node);
        }
    }
}

4.2 特殊解析

示例一:属性+常量(二元运算)

using System;
using System.Linq.Expressions;

namespace e2
{
    class Program
    {
        static void Main(string[] args)
        {
            Test1();
        }

        static void Test1()
        {
            Expression<Func<User, bool>> expression = (u) => u.age > 1;

            CustomVisitor visitor = new CustomVisitor();
            var exp = visitor.Modify(expression);
        }
    }

    public class User
    {
        public int uid { get; set; }
        public int age { get; set; }
    }

    public class CustomVisitor : ExpressionVisitor
    {
        public Expression Modify(Expression expression)
        {
            return this.Visit(expression);
        }

        // 二元运算类型
        protected override Expression VisitBinary(BinaryExpression node)
        {
            Console.WriteLine("VisitBinary");
            if (node.NodeType == ExpressionType.Add)
            {
                Expression left = this.Visit(node.Left);
                Expression right = this.Visit(node.Right);

                return Expression.Subtract(left, right);
            }
            return base.VisitBinary(node);
        }

        // 属性类型
        protected override Expression VisitMember(MemberExpression node)
        {
            Console.WriteLine("VisitMember");
            return base.VisitMember(node);
        }

        // 常量类型
        protected override Expression VisitConstant(ConstantExpression node)
        {
            Console.WriteLine("VisitConstant");
            return base.VisitConstant(node);
        }
    }
}

示例二:方法(如果要自定义解析处理的话需要预先知道方法名才可)

using System;
using System.Linq.Expressions;

namespace e2
{
    class Program
    {
        static void Main(string[] args)
        {
            Expression<Func<User, bool>> expression = (u) => u.name.Contains("1");
            CustomVisitor visitor = new CustomVisitor();
            visitor.Visit(expression);
        }
    }

    public class User
    {
        public int uid { get; set; }
        public int age { get; set; }
        public string name { get; set; }
    }

    public class CustomVisitor : ExpressionVisitor
    {
        public Expression Modify(Expression expression)
        {
            return this.Visit(expression);
        }

        // 方法表达式
        protected override Expression VisitMethodCall(MethodCallExpression node)
        {
            Console.WriteLine("VisitMethodCall:"+ node.Method.Name);
            return node;
        }
    }
}

5. 应用:解析Expression示例

5.1 示例一:简单示例,生成SQL

运算符扩展方法

using System;
using System.Linq.Expressions;

namespace e2
{
    internal static class SqlOperator
    {
        internal static string ToSqlOperator(this 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("不支持该方法");
            }
        }
    }
}

表达式树解析类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace e2
{
    public class ConditionBuilderVisitor : ExpressionVisitor
    {
        private Stack<string> _StringStack = new Stack<string>();

        public string Condition()
        {
            string condition = string.Concat(this._StringStack.ToArray());
            this._StringStack.Clear();
            return condition;
        }

        // 解析 二元表达式 类型
        protected override Expression VisitBinary(BinaryExpression node)
        {
            if (node == null) throw new ArgumentNullException("BinaryExpression");

            this._StringStack.Push(")");
            base.Visit(node.Right);//解析右边
            this._StringStack.Push(" " + node.NodeType.ToSqlOperator() + " ");
            base.Visit(node.Left);//解析左边
            this._StringStack.Push("(");

            return node;
        }
        
        // 解析 属性表达式 类型
        protected override Expression VisitMember(MemberExpression node)
        {
            if (node == null) throw new ArgumentNullException("MemberExpression");
            this._StringStack.Push(" [" + node.Member.Name + "] ");
            return node;
        }

        // 解析 常量表达式 类型
        protected override Expression VisitConstant(ConstantExpression node)
        {
            if (node == null) throw new ArgumentNullException("ConstantExpression");
            this._StringStack.Push(node.Value.ToString());
            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!");
            }
            this.Visit(m.Object);
            this.Visit(m.Arguments[0]);
            string right = this._StringStack.Pop();
            string left = this._StringStack.Pop();
            this._StringStack.Push(String.Format(format, left, right));

            return m;
        }
    }
}

调用执行

using System;
using System.Linq.Expressions;

namespace e2
{
    class Program
    {
        static void Main(string[] args)
        {
            Test1();
        }

        static void Test1()
        {
            Expression<Func<User, bool>> expression = (u) => u.age > 1;
            expression = (u) => u.age > 1 && u.uid < 2;
            expression = (u) => u.age > 1 && (u.uid < 2 || u.age > 2);
            expression = (u) => u.age > 1 && u.name.Contains("李");
            ConditionBuilderVisitor visitor = new ConditionBuilderVisitor();
            var exp = visitor.Visit(expression);
            Console.WriteLine(visitor.Condition());
        }
    }

    public class User
    {
        public int uid { get; set; }
        public int age { get; set; }
    }
}

5.2 示例二:表达式树链接,生成SQL

链接表达式扩展方法

using System;
using System.Linq.Expressions;

namespace e2
{
    // 建立新表达式
    internal 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);
        }
        // 利用ExpressionVisitor统一参数
        protected override Expression VisitParameter(ParameterExpression node)
        {
            return this._NewParameter;
        }
    }

    /// <summary>
    /// 合并表达式 And Or  Not扩展
    /// </summary>
    public static class ExpressionExtend
    {
        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> 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.And(left, right);
            return Expression.Lambda<Func<T, bool>>(body, newParameter);

        }
        
        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> 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<Func<T, bool>>(body, newParameter);
        }

        public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> expr)
        {
            var candidateExpr = expr.Parameters[0];
            var body = Expression.Not(expr.Body);

            return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
        }
    }
}

解析表达式类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace e2
{
    public class ConditionBuilderVisitor : ExpressionVisitor
    {
        private Stack<string> _StringStack = new Stack<string>();

        public string Condition()
        {
            string condition = string.Concat(this._StringStack.ToArray());
            this._StringStack.Clear();
            return condition;
        }

        // 解析 二元表达式 类型
        protected override Expression VisitBinary(BinaryExpression node)
        {
            if (node == null) throw new ArgumentNullException("BinaryExpression");

            this._StringStack.Push(")");
            base.Visit(node.Right);//解析右边
            this._StringStack.Push(" " + node.NodeType.ToSqlOperator() + " ");
            base.Visit(node.Left);//解析左边
            this._StringStack.Push("(");

            return node;
        }
        
        // 解析 属性表达式 类型
        protected override Expression VisitMember(MemberExpression node)
        {
            if (node == null) throw new ArgumentNullException("MemberExpression");
            this._StringStack.Push(" [" + node.Member.Name + "] ");
            return node;
        }

        // 解析 常量表达式 类型
        protected override Expression VisitConstant(ConstantExpression node)
        {
            if (node == null) throw new ArgumentNullException("ConstantExpression");
            this._StringStack.Push(node.Value.ToString());
            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!");
            }
            this.Visit(m.Object);
            this.Visit(m.Arguments[0]);
            string right = this._StringStack.Pop();
            string left = this._StringStack.Pop();
            this._StringStack.Push(String.Format(format, left, right));

            return m;
        }
    }
}

调用执行

using System;
using System.Linq.Expressions;

namespace e2
{
    class Program
    {
        static void Main(string[] args)
        {
            Test1();
        }

        static void Test1()
        {
            Expression<Func<User, bool>> expression = (u) => u.age > 1;
            expression = expression.And(x=>x.uid>2);
            expression = expression.Or(x=>x.uid>2);
            expression = expression.Not();
            ConditionBuilderVisitor visitor = new ConditionBuilderVisitor();
            var exp = visitor.Visit(expression);
            Console.WriteLine(visitor.Condition());
        }
    }

    public class User
    {
        public int uid { get; set; }
        public int age { get; set; }
        public string name { get; set; }
    }
}

5.3 示例三:实体映射

场景;DTO 类转换为 Model

方案一:手动,硬编码,不易出错,效率高,但太繁琐

[HttpPost]
public IActionResult Sava(UserDTD dtd)
{
    User user = new User
    {
      	uid = dtd.id,
        uname = dtd.name
    };
    _userBll.Sava(user);
}

方案二:使用反射,损耗高,两个类型的属性类型和名称需保证一致

/// <summary>
/// 反射映射
/// </summary>
public class ReflectionMapper
{
    /// <summary>
    /// 实体转换
    /// </summary>
    /// <typeparam name="T">传入类型</typeparam>
    /// <typeparam name="TResult">返回值类型</typeparam>
    /// <param name="tIn">传入参数</param>
    /// <returns>转换好的实体</returns>
    public static TResult Trans<T, TResult>(T tIn)
    {
        TResult tOut = Activator.CreateInstance<TResult>();
        foreach (var itemOut in tOut.GetType().GetProperties())
        {
            var propIn = tIn.GetType().GetProperty(itemOut.Name);
            itemOut.SetValue(tOut, propIn.GetValue(tIn));
        }

        foreach (var itemOut in tOut.GetType().GetFields())
        {
            var fieldIn = tIn.GetType().GetField(itemOut.Name);
            itemOut.SetValue(tOut, fieldIn.GetValue(tIn));
        }

        return tOut;
    }
}

方案三:序列化反序列化,损耗高,两个类型的属性类型和名称需保证一致

/// <summary>
/// 使用第三方序列化反序列化工具
/// </summary>
public class SerializeMapper
{
    /// <summary>
    /// 实体转换
    /// </summary>
    public static TResult Trans<T, TResult>(T tIn)
    {
        return JsonConvert.DeserializeObject<TResult>(JsonConvert.SerializeObject(tIn));
    }
}

方案四:表达式目录树 + 字典缓存

/// <summary>
/// 生成表达式目录树 字典缓存
/// </summary>
public class ExpressionMapper
{
    /// <summary>
    /// 字典缓存--hash分布
    /// </summary>
    private static Dictionary<string, object> _dic = new Dictionary<string, object>();

    /// <summary>
    /// 实体转换
    /// </summary>
    public static TResult Trans<T, TResult>(T tIn)
    {
        string key = string.Format("funckey_{0}_{1}", typeof(T).FullName, typeof(TResult).FullName);
        if (!_dic.ContainsKey(key))
        {
            ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "p");
            List<MemberBinding> memberBindingList = new List<MemberBinding>();
            foreach (var item in typeof(TResult).GetProperties())
            {
                MemberExpression property = Expression.Property(parameterExpression, typeof(T).GetProperty(item.Name));
                MemberBinding memberBinding = Expression.Bind(item, property);
                memberBindingList.Add(memberBinding);
            }
            foreach (var item in typeof(TResult).GetFields())
            {
                MemberExpression property = Expression.Field(parameterExpression, typeof(T).GetField(item.Name));
                MemberBinding memberBinding = Expression.Bind(item, property);
                memberBindingList.Add(memberBinding);
            }
            MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TResult)), memberBindingList.ToArray());
            Expression<Func<T, TResult>> lambda = Expression.Lambda<Func<T, TResult>>(memberInitExpression, new ParameterExpression[]
            {
                parameterExpression
            });
            Func<T, TResult> func = lambda.Compile(); //调用Compile方法将表达式转换成委托
            _dic[key] = func; //拼装是一次性的
        }

        return ((Func<T, TResult>)_dic[key]).Invoke(tIn);
    }
}

方案五:表达式目录树 + 泛型缓存(泛型缓存特点:为不同类型的组合去缓存一个结果)

/// <summary>
/// 生成表达式目录树  泛型缓存
/// </summary>
/// <typeparam name="T">传入参数类型</typeparam>
/// <typeparam name="TResult">返回值类型</typeparam>
public class ExpressionGenericMapper<T, TResult>
{
    /// <summary>
    /// 泛型缓存
    /// </summary>
    private static Func<T, TResult> _func = null;

    /// <summary>
    /// 静态构造函数(只会被调用一次)
    /// </summary>
    static ExpressionGenericMapper()
    {
        ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "p");
        List<MemberBinding> memberBindingList = new List<MemberBinding>();
        foreach (var item in typeof(TResult).GetProperties())
        {
            MemberExpression property = Expression.Property(parameterExpression, typeof(T).GetProperty(item.Name));
            MemberBinding memberBinding = Expression.Bind(item, property);
            memberBindingList.Add(memberBinding);
        }
        foreach (var item in typeof(TResult).GetFields())
        {
            MemberExpression property = Expression.Field(parameterExpression, typeof(T).GetField(item.Name));
            MemberBinding memberBinding = Expression.Bind(item, property);
            memberBindingList.Add(memberBinding);
        }
        MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TResult)), memberBindingList.ToArray());
        Expression<Func<T, TResult>> lambda = Expression.Lambda<Func<T, TResult>>(memberInitExpression, new ParameterExpression[]
        {
            parameterExpression
        });
        _func = lambda.Compile();//拼装是一次性的
    }

    /// <summary>
    /// 实体转换
    /// </summary>
    public static TResult Trans(T t)
    {
        return _func(t);
    }
}

6. 扩展补充

6.1 Lambda表达式本质

通过反编译工具得知,Lambda表达式,其实就是一个方法,在中间语言中,为其分配了一个方法名称(<>

6.2 新语法:扩展方法

注意事项

  • 实例方法优先于扩展方法(允许存在同名实例方法和扩展方法),注意优先级
  • 可以在空引用上调用扩展方法
  • 扩展方法必须放在一个非嵌套、非泛型的静态类中,可以被继承
  • 至少有一个参数,第一个参数必须附加this关键字,不能有任何其他修饰符(out/ref

编译结果

public static class Extend
{
    public static int ToInt(this int? k)
    {
        return k ?? 0;
    }
}
.class public auto ansi abstract sealed beforefieldinit lq1.Extend
	extends [mscorlib]System.Object
{
	.custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = (
		01 00 00 00
	)
	// Methods
	.method public hidebysig static 
		int32 ToInt (
			valuetype [mscorlib]System.Nullable`1<int32> k
		) cil managed 
	{
		.custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = (
			01 00 00 00
		)
		// Method begins at RVA 0x216c
		// Code size 13 (0xd)
		.maxstack 1
		.locals init (
			[0] int32
		)

		IL_0000: nop
		IL_0001: ldarga.s k
		IL_0003: call instance !0 valuetype [mscorlib]System.Nullable`1<int32>::GetValueOrDefault()
		IL_0008: stloc.0
		IL_0009: br.s IL_000b

		IL_000b: ldloc.0
		IL_000c: ret
	} // end of method Extend::ToInt

} // end of class lq1.Extend

6.3 Linq To Object/Sql

linq to object

声明的方法在 Enumerable 类中,针对于 Enumerable进行处理,数据来之内存数据

操作的表达式是一个委托

inq to sql

声明的方法在 Queryable 类中,针对于 Queryable进行处理,数据来之内存数据或来自数据库的的数据源

操作的表达式是一个表达式目录树,通过表达式目录树解析成SQL语句

6.4 yield 迭代器

using System;
using System.Collections.Generic;

namespace lq2
{
    class Program
    {
        static void Main(string[] args)
        {
            List<User> list = new List<User>
            {
                new User(){uid=1,uname="a1",age=18,gender=0 },
                new User(){uid=2,uname="a2",age=28,gender=1 },
                new User(){uid=3,uname="a3",age=23,gender=1 },
                new User(){uid=4,uname="a4",age=18,gender=0 },
                new User(){uid=5,uname="a5",age=33,gender=1 }
            };

            var d1 = list.MyWhere(x => x.uid > 3);
            foreach (var item in d1)
            {
                Console.WriteLine(item.uid);
            }
        }
    }

    public class User
    {
        public int uid { get; set; }
        public int age { get; set; }
        public string uname { get; set; }
        public int gender { get; set; }
    }

    public static class Extend
    {
        public static IEnumerable<T> MyWhere<T>(this IEnumerable<T> rouse, Func<T, bool> func)
        {
            
            foreach (var item in rouse)
            {
                if (func(item))
                {
                    // yield 迭代器通常与 Enumerable共同使用,实现按需获取(延迟加载)
                    yield return item;  
                }
            }
        }
    }
}

6.5 表达式目录树与委托

Expression一般都是都是配合委托一起来使用的,比如和委托Action,Func

Expression<Func<T>>是可以转成Func的(通过compile()方法转换),反之则不行

6.6 ORM与表达式树目录的关系

平常项目中经常用到的EF操作时的扩展方法(Where之类的)其实传的就是表达式目录树

05.表达式目录树Expression

上一篇:素数筛


下一篇:Codeforces Round #737 (Div. 2) C. Moamen and XOR(数位DP)