• 在实际应用中无论是用于查询还是其他方面我们或多或少都会想能不能动态表达式树来降低开发时间。比如如果查询时用拼写SQL形式查询,我们可以写一个SQL拼接的方法,这个方法通过字符串拼接外部传入的 “WHERE”就能...

    前言

    通过上篇我们大概了解了下表达式树大概是什么,那本篇将带来表达式树的一些进阶应用。

    动态构建表达式树

    在实际应用中无论是用于查询还是其他方面我们或多或少都会想能不能动态表达式树来降低开发时间。比如如果查询时用拼写SQL形式查询,我们可以写一个SQL拼接的方法,这个方法通过字符串拼接外部传入的 “WHERE”就能比较灵活的写出一个通用查询方法。而此时如果换成通过写表达式树的形式的话我们能不能类似于以前的哪种字符串拼接的方式拼接出一个我们想要的表达式树呢?答案时能的。表达式树在程序运行时其就是一个表达式树对象,我们只需要参照MSDN中的文档就能创建出任意我们想要的表达式树对象,只是这可能会比较麻烦。
    那么这里我们可以选择自己花时间自己好好研究下表达式树,也可以用现有的第三方工具(库)比如DynamicExpresso

    动态创建委托

    依旧直接上代码

      /// <summary>
        /// 账号金额
        /// </summary>
        public class AccountAmount
        {
            public int Id { get; set; }
            /// <summary>
            /// 冻结金额
            /// </summary>
            public int Frozen { get; set; }
            /// <summary>
            /// 可用金额
            /// </summary>
            public int Available { get; set; }
    
            /// <summary>
            /// 计算总额
            /// </summary>
            /// <returns></returns>
            public int ComputeAllAmount ()
            {
                return Frozen + Available;
            }
        }
    
        internal class Program
        {
            private static void Main (string [ ] args)
            {
                AccountAmount accountAmount=new AccountAmount()
                {
                    Id=0,
                    Frozen=50,
                    Available=100
                };
    
                Interpreter target = new Interpreter();//创建 解释器
    
                Func<AccountAmount , int> getForzenFunc = target.ParseAsDelegate<Func<AccountAmount , int>>("accountAmount.Frozen" , "accountAmount");//创建一个获取冻结金额的Func
                Console.WriteLine("获取冻结金额:" + getForzenFunc(accountAmount));
    
                Func<AccountAmount , int> callComputeAllAmountFunc = target.ParseAsDelegate<Func<AccountAmount , int>>("accountAmount.ComputeAllAmount()" , "accountAmount");//创建一个调用计算总额方法的Func
                Console.WriteLine("调用计算总额方法:" + callComputeAllAmountFunc(accountAmount));
    
                Func<AccountAmount , int> computeAllAmountFunc = target.ParseAsDelegate<Func<AccountAmount , int>>("accountAmount.Frozen+accountAmount.Available" , "accountAmount");//创建一个计算总额的Func
                Console.WriteLine("计算总额:" + computeAllAmountFunc(accountAmount));
    
                Console.ReadKey();
            }
    
        }
    

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

    从上可以看出,我们可以通过组装表达式树动态的生成委托,并通过调用委托完成对应的操作。

    动态创建Linq查询表达式树

     /// <summary>
        /// 账号金额
        /// </summary>
        public class AccountAmount
        {
            public int Id { get; set; }
            /// <summary>
            /// 冻结金额
            /// </summary>
            public int Frozen { get; set; }
            /// <summary>
            /// 可用金额
            /// </summary>
            public int Available { get; set; }
    
            public override string ToString ()
            {
                return string.Format("ID:{0},Frozen:{1},Available:{2}" , Id , Frozen , Available);
            }
        }
    
        internal class Program
        {
            private static void Main (string [ ] args)
            {
                List<AccountAmount> accountAmountList =new List<AccountAmount>();
                accountAmountList.Add(new AccountAmount()
                {
                     Id=1,
                     Frozen=50,
                     Available=100,
                });
                accountAmountList.Add(new AccountAmount()
                {
                    Id = 2 ,
                    Frozen = 20 ,
                    Available = 200 ,
                });
                accountAmountList.Add(new AccountAmount()
                {
                    Id = 3 ,
                    Frozen = 70 ,
                    Available = 50 ,
                });
                
                Interpreter target = new Interpreter();//创建 解释器
    
                Func<AccountAmount , bool> whereFunc = target.ParseAsDelegate<Func<AccountAmount , bool>>("accountAmount.Frozen <50" , "accountAmount");//创建一个冻结金额小于50的Func
                Console.WriteLine("冻结金额小于50的账号:"  );
                var data1= accountAmountList.Where(whereFunc);
                foreach (var a in data1 )
                {
                    Console.WriteLine(a.ToString());
                }
    
                System.Linq.Expressions.Expression<Func<AccountAmount,bool>> whereExpression=target.ParseAsExpression<Func<AccountAmount , bool>>("accountAmount.Frozen >50" , "accountAmount");//创建一个冻结金额大于50的表达式树
               var data2= accountAmountList
                    .AsQueryable()//模拟使用EF查询
                    .Where(whereExpression)
                    .ToList();
                Console.WriteLine("冻结金额大于50的账号:");
                foreach ( var a in data2 )
                {
                    Console.WriteLine(a.ToString());
                }
                Console.ReadKey();
            }
    
        }
    

    执行结果
    在这里插入图片描述
    这里直接传递了字符串,通过字符串去解析创建了委托和表达式树。实际应用中可以根据实际情况来拼接这个字符串。

    展开全文
  • 一般如果逻辑比较简单,只是存在有的情况多一个查询条件,有的情况不需要添加该查询条件 简单方式这样操作就可以了 public IQueryable DynamicChainedSyntax (IQueryable files, bool pastOnly) ...

    一般如果逻辑比较简单,只是存在有的情况多一个查询条件,有的情况不需要添加该查询条件

    简单方式这样操作就可以了

    public IQueryable<FileImport> DynamicChainedSyntax
    		(IQueryable<FileImport> files, bool pastOnly)
    {
    	var query = files.Where(file => file.ImportDate >
    				DateTime.Now.AddDays(-7));
    	if (pastOnly)
    		query = query.Where(file => file.ImportDate <
    				DateTime.Today);
    	return query;
    }

    这里的多个where条件是AND关系,如果是OR的关系,可将多次查询的结果进行union


    当然大多数的时候,我们是希望能够动态构建查询条件的,你可以针对任何字段进行任何操作符形式的查询,不同查询条件之间的关系也是可以动态定义的。

    这时候表达式树就派上用场了,关于表达式树的基础知识已经在上一篇中提到了。

    这里主要说如何构建linq中Where查询条件,其实只是熟悉表达式树的其他提供的方法,非常简单。

    public Func<TSource, bool> SimpleComparison<TSource>			
    			string property, object value)
    {
    	var type = typeof (TSource);
    	var pe = Expression.Parameter(type, "p");
    	var propertyReference = Expression.Property(pe, property);
    	var constantReference = Expression.Constant(value);
    	return Expression.Lambda<Func<TSource, bool>>
    		(Expression.Equal(propertyReference, constantReference),
    		new[] { pe }).Compile();
    }

    呵呵,话到这里,看看我的小DEMO

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                //(a,b)=>(a+b)
                //参数的构建  (定义参数的名字和参数的类型)
                ParameterExpression exp1 = Expression.Parameter(typeof(int), "a");
                ParameterExpression exp2 = Expression.Parameter(typeof(int), "b");
                //表达式主体的构建 
                BinaryExpression exp = Expression.Add(exp1, exp2);
                //表达式树的构建(如下定义,表达式的类型为Lambda 
                //lambda表达式的类型为Func<int, int, int>)
                var lambda = Expression.Lambda<Func<int, int, int>>(exp, exp1, exp2);
    
                //p=>p.Name 可以动态构建OrderBy
                ParameterExpression exp3 = Expression.Parameter(typeof(Person), "p");
                var property = Expression.Property(exp3, "Name");
                var lambda2 = Expression.Lambda<Func<Person, string>>(property, exp3);
    
                //p=>p.Name == "daisy" 
                List<Person> persons = new List<Person>() 
                { new Person(){ Name = "daisy", age = 10 },
                  new Person(){ Name = "daisy", age = 12 }, 
                  new Person(){Name="dom", age=12},
                  new Person(){Name="caren", age=10}};
                var compareExp = simpleCompare<Person>("Name", "daisy");
                var daisys = persons.Where(compareExp).ToList();
                foreach (var item in daisys)
                {
                    Console.WriteLine("Name:  "+item.Name+"    Age:  "+item.age);
                }
                Console.ReadKey();
            }
            public static Func<TSource, bool> simpleCompare<TSource>(string property, object value)
            {
                var type = typeof(TSource);
                var pe = Expression.Parameter(type, "p");
                var propertyReference = Expression.Property(pe, property);
                var constantReference = Expression.Constant(value);
    
                //compile 是表达式的一个接口,生成该lambda表达式树对的委托
                return Expression.Lambda<Func<TSource, bool>>(Expression.Equal(propertyReference, constantReference), pe).Compile();
            }
    
        }
        public class Person
        {
            public string Name { get; set; }
            public int age { get; set; }
        }
    }
    

    再来看看查询结果:


    嗯,理解起来还是非常简单的,就是构建表达式树,返回我们需要的委托类型!


    接下来猛料哦

    动态构建表达式树,最佳实践版,很实用!

     public class FilterCollection : Collection<IList<Filter>>
        {
            public FilterCollection()
                : base()
            { }
        }
    
        public class Filter
        {
            public string PropertyName { get; set; }
            public Op Operation { get; set; }
            public object Value { get; set; }
        }
    
        public enum Op
        {
            Equals,
            GreaterThan,
            LessThan,
            GreaterThanOrEqual,
            LessThanOrEqual,
            Contains,
            StartsWith,
            EndsWith
        }

    通过上面的类可以动态构建复杂的查询条件,下面具体调用的类哦

    using Infrastructure.Model;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Reflection;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Infrastructure.Operation
    {
        public static class LambdaExpressionBuilder
        {
            private static MethodInfo containsMethod = typeof(string).GetMethod("Contains");
            private static MethodInfo startsWithMethod =
                                    typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) });
            private static MethodInfo endsWithMethod =
                                    typeof(string).GetMethod("EndsWith", new Type[] { typeof(string) });
            private static Expression GetExpression(ParameterExpression param, Filter filter)
            {
                MemberExpression member = Expression.Property(param, filter.PropertyName);
                Expression handledMember = member;
                ConstantExpression constant = Expression.Constant(filter.Value);
    
                if (member.Member.MemberType == MemberTypes.Property)
                {
                    Type propertyType = ((PropertyInfo)member.Member).PropertyType;
                    if (propertyType == typeof(string))
                    {
                        handledMember = Expression.Call(member, typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
                    }
                    if (propertyType == typeof(DateTime?))
                    {
                        handledMember = Expression.Property(member, typeof(DateTime?).GetProperty("Value"));
                    }
                }
    
                switch (filter.Operation)
                {
                    case Op.Equals:
                        return Expression.Equal(handledMember, constant);
                    case Op.GreaterThan:
                        return Expression.GreaterThan(handledMember, constant);
                    case Op.GreaterThanOrEqual:
                        return Expression.GreaterThanOrEqual(handledMember, constant);
                    case Op.LessThan:
                        return Expression.LessThan(handledMember, constant);
                    case Op.LessThanOrEqual:
                        return Expression.LessThanOrEqual(handledMember, constant);
                    case Op.Contains:
                        return Expression.Call(handledMember, containsMethod, constant);
                    case Op.StartsWith:
                        return Expression.Call(handledMember, startsWithMethod, constant);
                    case Op.EndsWith:
                        return Expression.Call(handledMember, endsWithMethod, constant);
                }
    
                return null;
            }
            private static BinaryExpression GetORExpression(ParameterExpression param, Filter filter1, Filter filter2)
            {
                Expression bin1 = GetExpression(param, filter1);
                Expression bin2 = GetExpression(param, filter2);
    
                return Expression.Or(bin1, bin2);
            }
    
            private static Expression GetExpression(ParameterExpression param, IList<Filter> orFilters)
            {
                if (orFilters.Count == 0)
                    return null;
    
                Expression exp = null;
    
                if (orFilters.Count == 1)
                {
                    exp = GetExpression(param, orFilters[0]);
                }
                else if (orFilters.Count == 2)
                {
                    exp = GetORExpression(param, orFilters[0], orFilters[1]);
                }
                else
                {
                    while (orFilters.Count > 0)
                    {
                        var f1 = orFilters[0];
                        var f2 = orFilters[1];
    
                        if (exp == null)
                        {
                            exp = GetORExpression(param, orFilters[0], orFilters[1]);
                        }
                        else
                        {
                            exp = Expression.Or(exp, GetORExpression(param, orFilters[0], orFilters[1]));
                        }
                        orFilters.Remove(f1);
                        orFilters.Remove(f2);
    
                        if (orFilters.Count == 1)
                        {
                            exp = Expression.Or(exp, GetExpression(param, orFilters[0]));
                            orFilters.RemoveAt(0);
                        }
                    }
                }
    
                return exp;
            }
    
            public static Expression<Func<T, bool>> GetExpression<T>(FilterCollection filters)
            {
                if (filters == null || filters.Count == 0)
                    return null;
    
                ParameterExpression param = Expression.Parameter(typeof(T), "t");
                Expression exp = null;
    
                if (filters.Count == 1)
                {
                    exp = GetExpression(param, filters[0]);
                }
                else if (filters.Count == 2)
                {
                    exp = Expression.AndAlso(GetExpression(param, filters[0]), GetExpression(param, filters[1]));
                }
    
                else
                {
                    while (filters.Count > 0)
                    {
                        var f1 = filters[0];
                        var f2 = filters[1];
                        var f1Andf2 = Expression.AndAlso(GetExpression(param, filters[0]), GetExpression(param, filters[1]));
                        if (exp == null)
                        {
                            exp = f1Andf2;
                        }
                        else
                        {
                            exp = Expression.AndAlso(exp, f1Andf2);
                        }
    
                        filters.Remove(f1);
                        filters.Remove(f2);
    
                        if (filters.Count == 1)
                        {
                            exp = Expression.AndAlso(exp, GetExpression(param, filters[0]));
                            filters.RemoveAt(0);
                        }
                    }
                }
    
                return Expression.Lambda<Func<T, bool>>(exp, param);
            }
        }
    }
    

    再来一个OrderBy动态构建

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Reflection;
    using System.Text;
    
    namespace Jurassic.Sooil.Com
    {
        public static class OrderExpression
        {
            public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property)
            {
                return ApplyOrder<T>(source, property, "OrderBy");
            }
            public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string property)
            {
                return ApplyOrder<T>(source, property, "OrderByDescending");
            }
            public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string property)
            {
                return ApplyOrder<T>(source, property, "ThenBy");
            }
            public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> source, string property)
            {
                return ApplyOrder<T>(source, property, "ThenByDescending");
            }
            static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName)
            {
                string[] props = property.Split('.');
                Type type = typeof(T);
                ParameterExpression arg = Expression.Parameter(type, "x");
                Expression expr = arg;
                foreach (string prop in props)
                {
                    // use reflection (not ComponentModel) to mirror LINQ
                    PropertyInfo pi = type.GetProperty(prop);
                    expr = Expression.Property(expr, pi);
                    type = pi.PropertyType;
                }
                Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
                LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);
    
                object result = typeof(Queryable).GetMethods().Single(
                        method => method.Name == methodName
                                && method.IsGenericMethodDefinition
                                && method.GetGenericArguments().Length == 2
                                && method.GetParameters().Length == 2)
                        .MakeGenericMethod(typeof(T), type)
                        .Invoke(null, new object[] { source, lambda });
                return (IOrderedQueryable<T>)result;
            } 
        }
    }
    

    至此动态构建LINQ查询结束!花了上班时间一上午,还是相当值得的,不过被项目经理知道了得哭死!

    不管如何,学到手的才是自己的!



    展开全文
  • C#表达式树

    2019-04-16 10:38:27
    C#表达式树 表达式和表达式树 表达式可以是一个参数(如参数x),一个常数(如常数5),一个加运算(如x+5)等等,可以把几个小的表达式组装在一起成为大的表达式,例如:(x+5)-(++y)。对于这样一个表达式可以用一...

    C#表达式树


     

    表达式和表达式树

    表达式可以是一个参数(如参数x),一个常数(如常数5),一个加运算(如x+5)等等,可以把几个小的表达式组装在一起成为大的表达式,例如:(x+5)-(++y)。对于这样一个表达式可以用一棵树来表示,如下:

    这就是表达式树,表达式树本身也是一个表达式(大的表达式)。一个表达式也是一棵表达式树,可以说它是一棵小的表达式树。可以把表达式树和表达式认为是一个东西,C#中都用Expression类表示。

     

    表达式树结构

    对于一棵表达式树,其叶子节点都是参数或者常数,非叶子节点都是运算符或者控制符。

    C#中的Expression类就是表达式类,在C#中表达式树的节点总共有85种,这些类型全部由枚举变量ExpressionType决定。Expression类成员变量NodeType记录了表达式树根节点的节点类型。表达式树还有一个值类型,由Expression类成员变量Type决定。例如表达式(x+5)-(++y) NodeType为ExpressionType.Subtract(减),Type为typeof(int)。

     

    表达式树的创建

    Lambda表达式方法

    表达式可以通过Lambda表示创建Expression<TDelegate>类型,如下:

    Expression<Func<int, int,bool>> fun = (x, y) => x < y

    这种方法创建出的表达式根节点类型为ExpressionType.Lambda,Type类型为返回值类型typeof(bool)。

    这里的表达式根节点类型为Lambda并不是LessThan(小于)。Expression<TDelegate>有一个成员变量Body,这个成员变量是“lambda表达式的式子”,在例子中fun.Body为x<y,fun.Body的节点类型为LessThan,这里的x<y只是fun的一棵子树。

     

    组装法

    另一种创建表示的方式是通过Expression类的静态函数“组装”表达式如下:

     ParameterExpression p1 = Expression.Parameter(typeof(int),"x");

     ParameterExpression p2 = Expression.Parameter(typeof(int),"y");

      BinaryExpression expr =Expression.GreaterThan(p1, p2);

    我们先创建了两个参数表达式x, y然后用GreaterThan组装在一起,最终的表达式为“x>y”,expr的节点类型为GreaterThan,Type类型为typeof(bool)

    Expression类中有我们所需要所有“组装”表达式的用的静态函数,我们不能直接用new的方法去创建表达式节点,表达式所有的节点需要用Expression类中的静态函数创建。

    更多关于如何创建表示请参考:

    http://www.cnblogs.com/jesse2013/p/expressiontree-part1.html

    http://www.cnblogs.com/jesse2013/p/expressiontree-part2.html

     

    节点类与节点类型

    从创建表达式可以发现表达式不同类型节点会对应不同的类,如ParameterExpression,BinaryExpression,Expression<TDelegate>等等,这些类都继承了Expression类,所以它们都表示表达式,只是他们的根节点的节点类型不一样。

    表达式节点类和表达式类型是有一定对应关系的,下面有一部分总结但不完整。

    类型

    类名

    可对应的节点类型

    常量表达式

    ConstantExpression

    ExpressionType.Constant

    参数表达式

    ParameterExpression

    ExpressionType.Parameter

    一元运算符表达式

    UnaryExpression

    ExpressionType.Negate

    ExpressionType.NegateChecked

    ExpressionType.Not

    ExpressionType.Convert

    ExpressionType.ConvertChecked

    ExpressionType.ArrayLength

    ExpressionType.Quote

    ExpressionType.TypeAs

    二元运算符表达式

    BinaryExpression

    ExpressionType.Add

    ExpressionType.AddChecked

    ExpressionType.Subtract

    ExpressionType.SubtractChecked

    ExpressionType.Multiply

    ExpressionType.MultiplyChecked

    ExpressionType.Divide

    ExpressionType.Modulo

    ExpressionType.And

    ExpressionType.AndAlso

    ExpressionType.Or

    ExpressionType.OrElse

    ExpressionType.LessThan

    ExpressionType.LessThanOrEqual

    ExpressionType.GreaterThan

    ExpressionType.GreaterThanOrEqual

    ExpressionType.Equal

    ExpressionType.NotEqual

    ExpressionType.Coalesce

    ExpressionType.ArrayIndex

    ExpressionType.RightShift

    ExpressionType.LeftShift

    ExpressionType.ExclusiveOr

    is运算符表达式

    TypeBinaryExpression

    ExpressionType.TypeIs

    条件表达式

    ConditionalExpression

    ExpressionType.Conditional

    访问字段或属性表达式

    MemberExpression

    ExpressionType.MemberAccess

    调用成员函数表达式

    MethodCallExpression

    ExpressionType.Call

    Lambda表达式

    LambdaExpression

    ExpressionType.Lambda

    委托表达式

    Expression<TDelegate>

    ExpressionType.Lambda

    new运算符表达式

    NewExpression

    ExpressionType.New

    new数组表达式

    NewArrayExpression

    ExpressionType.NewArrayInit

    ExpressionType.NewArrayBounds

    调用其他表达式的表达式

    InvocationExpression

    ExpressionType.Invoke

    属性初始表达式

    MemberInitExpression

    ExpressionType.MemberInit

    列表初始表达式

    ListInitExpression

    ExpressionType.ListInit

     

    表达式的运行

    实际上大多数表达式都不能运行,只有LambdaExpression和Expression<TDelegate>表达式可以运行,因为这两种表达式相当于一个函数。

    这两种表达式有一个成员函数Compile,可把表达式转换成一个Fun或Action函数对象。如下:

                Expression<Func<int,int>> Fun = x => x + 6;

                var f = Fun.Compile();

                f(5);

    其他类型表达式如果想要运行,我们可以通过Expression类的静态函数Lambda<TDelegate>转换成相应的Expression<TDelegate>节点,然后运行。如下运行我们自己组装的表达式:

      ParameterExpression p1 = Expression.Parameter(typeof(int), "x");

      ParameterExpression p2 = Expression.Parameter(typeof(int), "y");

      BinaryExpression expr = Expression.GreaterThan(p1, p2);

      Expression<Func<int, int, bool>> FunGreater =

     Expression.Lambda<Func<int, int, bool>>(expr, p1, p2);

      Console.WriteLine(FunGreater.ToString());

      Console.WriteLine(FunGreater.Compile().Invoke(5, 2));

     

    遍历表达式树

    表达式树是一棵树,我们可以遍历这棵树,在遍历的时候我们可以替换掉原来表达式中的参数、操作、常数来构建一颗新的表达式树。

     

    遍历表达式树

    可以通过ExpressionVisitor类来遍历一棵表达式树。我们需要重写ExpressionVisitor类的一些函数来达到我们的目的如重写Expression VisitParameter(ParameterExpressionnode)函数,这个函数是当遍历到参数节点时调用。node参数是表达式中的参数,而我们的返回值将会替换当前的表达式中的参数。

     遍历顺序

    遍历的顺序可以由代码控制,可以是先遍历根节点再遍历子树,或者先遍历子树后遍历根节点,然后从左到右遍历子树。

     

    以下创建了一个先序遍历的类,即先遍历根节点,然后从左到右遍历子树。

        internal class ExpVisitor : ExpressionVisitor
        {
            protected override Expression VisitBinary(BinaryExpression node)
            {
                Console.WriteLine($"Binary Type = {node.NodeType}");
                return base.VisitBinary(node);//继续遍历节点的子树
            }
    
            protected override Expression VisitConstant(ConstantExpression node)
            {
                Console.WriteLine($"Constant = {node.Value}");
                return node;//因为是叶子节点,所以可以直接返回
            }
    
            protected override Expression VisitParameter(ParameterExpression p)
            {
                Console.WriteLine($"ParamName = {p.Name}");
                return p;//因为是叶子节点,所以可以直接返回
            }
        }

    利用上面的遍历类遍历表达式 (x <10 && x<3) || (y >= z)

    遍历的结果如下:

     

    替换参数

    以下是一个替换参数的遍历类

        internal class ExpressionParameterReplacer : ExpressionVisitor
        {
            public ExpressionParameterReplacer()
            {
                var newParam = Expression.Parameter(typeof(int), "NewParam");
                this.ParameterExpression = newParam;
            }
    
            public ParameterExpression ParameterExpression { get; private set; }
    
            public Expression Replace(Expression expression)
            {
                return this.Visit(expression);
            }
    
            protected override Expression VisitParameter(ParameterExpression _)
            {
                return this.ParameterExpression;
            }
        }
    

     

    使用该类替换表达式(x <10 && x<3) || (y >= z)中所有的参数,然后遍历输出,替换后生成的表达式为(NewParam < 10&& NewParam <3)|| (NewParam >= NewParam),三个参数都变成了NewParam。

     

    结果如下:

      

    两个例子完整的代码如下:

        internal class ExpressionParameterReplacer : ExpressionVisitor
        {
            public ExpressionParameterReplacer()
            {
                var newParam = Expression.Parameter(typeof(int), "NewParam");
                this.ParameterExpression = newParam;
            }
    
            public ParameterExpression ParameterExpression { get; private set; }
    
            public Expression Replace(Expression expression)
            {
                return this.Visit(expression);
            }
    
            protected override Expression VisitParameter(ParameterExpression _)
            {
                return this.ParameterExpression;
            }
        }
        internal class ExpVisitor : ExpressionVisitor
        {
            protected override Expression VisitBinary(BinaryExpression node)
            {
                Console.WriteLine($"Binary Type = {node.NodeType}");
                return base.VisitBinary(node);//继续遍历节点的子树
            }
    
            protected override Expression VisitConstant(ConstantExpression node)
            {
                Console.WriteLine($"Constant = {node.Value}");
                return node;//因为是叶子节点,所以可以直接返回
            }
    
            protected override Expression VisitParameter(ParameterExpression p)
            {
                Console.WriteLine($"ParamName = {p.Name}");
                return p;//因为是叶子节点,所以可以直接返回
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine($"Visiter Tree:");
                Expression<Func<int, int, int, bool>> FunX1 = (x, y, z) => (x < 10 && x>3) || (y >= z);
                Console.WriteLine(FunX1.Body.ToString());
                ExpVisitor v2 = new ExpVisitor();
                v2.Visit(FunX1.Body);
    
                Console.WriteLine($"\n\nReplace Param:");
                ExpressionParameterReplacer v = new ExpressionParameterReplacer();
                var temp = v.Visit(FunX1.Body);
                Console.WriteLine(temp.ToString());
                v2.Visit(temp);
    
                Expression<Func<int, bool>> ReplaceExp = Expression.Lambda<Func<int, bool>>(temp, v.ParameterExpression);
                ReplaceExp.Compile().Invoke(5);
    
                Console.WriteLine($"\n\n");
                ParameterExpression p1 = Expression.Parameter(typeof(int), "x");
                ParameterExpression p2 = Expression.Parameter(typeof(int), "y");
                BinaryExpression expr = Expression.GreaterThan(p1, p2);
                Expression<Func<int, int, bool>> FunGreater = Expression.Lambda<Func<int, int, bool>>(expr, p1, p2);
                Console.WriteLine(FunGreater.ToString());
                Console.WriteLine(FunGreater.Compile().Invoke(5, 2));
            }
        }
    

    输出结果:

     

     

    展开全文
  • 以编程方式构建表达式树 System.Linq.Expressions命名空间包含了代表表达式的各个类,它们都继承自Expression Expression类包含两个属性 Type属性代表表达式求值后的.NET类型,可把它视为一个返回类型,例如,...
    • 以编程方式构建表达式树

    1. System.Linq.Expressions命名空间包含了代表表达式的各个类,它们都继承自Expression
    2. Expression类包含两个属性
    • Type属性代表表达式求值后的.NET类型,可把它视为一个返回类型,例如,如果一个表达式要获取一个字符串的Length属性,该表达式的类型是int。
    • NodeType属性返回所代表的表达式的种类。它是ExpressionType枚举的成员,包括LessThan, Multiply和Invoke等。仍然使用上面的例子,对于MyString.Length这个属性访问来说,其节点类型是MemberAccess。
       private static void TestExpression()
            {
                Expression firstArg = Expression.Constant(2);
                Expression secondArg = Expression.Constant(3);
                Expression add = Expression.Add(firstArg, secondArg);
                Console.WriteLine(add);
            }

    • 将表达式树编译成委托

     private static void TestExpression()
            {
                Expression firstArg = Expression.Constant(2);
                Expression secondArg = Expression.Constant(3);
                Expression add = Expression.Add(firstArg, secondArg);
                Func<int> compiled = Expression.Lambda<Func<int>>(add).Compile();
                Console.WriteLine(add);
            }
    1. 简单加法表达式就是一个不获取任何参数,并返回整数的委托。与之匹配的签名就是Func<int>,所以可以使用一个Expression<Func<int>>。
    • 将C# Lambda表达式转换成表达式树

      private static void TurnLambdatoExpression()
            {
                Expression<Func<int>> return5 = () => 5;
                Func<int> compiled = return5.Compile();
                Console.WriteLine(compiled());
            }
    1. 并非所有的Lambda表达式都能转换成表达式树。不能将带有一个语句块(即使只有一个return语句)的Lambda转换成表达式树
    2. 只有对单个表达式进行求值的Lambda才可以。
    3. 表达式中还不能包含赋值操作。
    • 用代码来构造一个方法调用表达式树

     private static void ManuallyCreateExpression()
            {
                MethodInfo method = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
                var target = Expression.Parameter(typeof(string), "x");
                var methodArg = Expression.Parameter(typeof(string), "y");
                Expression[] methodArgs = new[] { methodArg };
                Expression call = Expression.Call(target, method, methodArgs);
                var lambdaParameters = new[] { target, methodArg };
                var lambda = Expression.Lambda<Func<string, string, bool>>(call, lambdaParameters);
                var compiled = lambda.Compile();
                Console.WriteLine(compiled("First", "Second"));
                Console.WriteLine(compiled("First", "Fir"));
            }
    1. 为了构造最终的方法调用表达式,我们需要知道方法调用的几个部件:方法的目标(调用StartsWith的字符串),方法本身(MethodInfo);参数列表(本例只有一个参数)。
    2. 将方法调用构造成一个表达式之后,接着需要把它转化成Lambda。
    • 位于LINQ核心的表达式树

    1. LINQ提供的中心思想在于,我们可以从一个熟悉的源语言(C#)生成一个表达式树,将结果作为一个中间格式,再将其转换成目标平台上的本地语言,比如SQL。
    • LINQ之外的表达式树

    1. 优化动态语言运行时
    2. 可以放心的对成员的引用进行重构
    3. 更简单的反射
    展开全文
  • 环境: window 10 vs2019 16.5.1 .netcore 3.1 ...C# 表达式树讲解(一) 微软文档(Expression 类) 目的: 探索什么是表达式树? 表达式能用来做什么? 表达式树在Linq To Sql中起到了什么作用? 一...

    环境:

    • window 10
    • vs2019 16.5.1
    • .netcore 3.1
    • .Net Reflector 10
    • ILSpy版本6.0.0.5559-preview2

    参考:

    目的:
    探索什么是表达式树?
    表达式能用来做什么?
    表达式树在Entity Framework中起到了什么作用?

    一、什么是表达式树

    在c#中,我们可以定义一种树状的数据结构来描述c#中的代码,这种树状的数据结构就是表达式树,也称之为表达式(各种表达式之间是可以相互嵌套的)。比如说:(5-2)+(2+3)这个表达式,拆分成树状结构如下图:
    在这里插入图片描述
    当然,c#代码肯定比这个要复杂的多,比如:你的定义语句、循环、判断、属性访问等等。。。
    在c#中,微软为每中运算类型的代码定义了不同的表达式类型,它们有共同的基类:Expression。

    表达式树基类 Expression(抽象类)

    它表示所有出现在c#中的代码的类型,主要包含两个属性:

    • NodeType:这个表达式的节点类型,这是一个枚举,c#中定义了85个节点类型
      在这里插入图片描述
    • Type:这个表达式的静态类型,也就是这个表达式的返回类型

    二、常用的表达式类型有哪些?

    我们直接看有哪些表达式类是继承自Expression的:
    在这里插入图片描述
    当然还有没有直接继承Expression的,比如:Expression<T>(我们最常用的表达式树,继承自LambdaExpression),这里我就挑选几个表达式树简单描述一下:

    • BinaryExpression:二元运算表达式
        1). Right:二元运算符的右侧表达式(Expression)
        2). Left:二元运算符的左侧表达式(Expression)
    • ConstantExpression:常量表达式
        1). Value:常量值,Object
    • ConditionalExpression:条件表达式
        1). IfFalse:为False时的表达式(Expression)
        2). IfTrue:为True时的表达式(Expression)
        3). Test:判断表达式
    • ParameterExpression:参数表达式
        1). IsByRef:参数是否传引用
        2). Name:参数名称
    • LambdaExpression:lambda表达式
        1).Body:内容表达式(Expression)
        2).Parameters:参数表达式集合(ReadOnlyCollection)
        3).ReturnType:返回的类型
        4).Compile():方法,编译生成委托
    • Expression<T>:带有泛型的表达式,一般这个T就是委托类型的,继承自LabelExpression

    三、怎么创建表达式树

    3.1 纯手工组装

    3.1.1 将简单lambda表达式:(x,y)=>x+y用表达式树的形式表示出来

    static void Main(string[] args)
    {
         //组装表达式树
         ParameterExpression para1 = Expression.Parameter(typeof(int), "x");
         ParameterExpression para2 = Expression.Parameter(typeof(int), "y");
         var paras = new ParameterExpression[] { para1, para2 };
         BinaryExpression body = Expression.Add(para1, para2);
         var expression = Expression.Lambda(body, paras);
         //编译表达式树
         var func = expression.Compile() as Func<int, int, int>;
         //调用执行
         var res = func(1, 2);
         Console.WriteLine($"表达式树的执行结果:(1,2)=>{res}");
         Console.WriteLine("Hello World!");
         Console.ReadLine();
     }
    

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

    3.1.2 将复杂的方法定义用表达式树的形式表示出来

    原方法定义:

    public static string GetScoreDesc(string name, int score, int standard)
    {
        string desc = "";
        if (score > standard)
        {
            desc = "已及格";
        }
        else if (score == standard)
        {
            desc = "刚及格";
        }
        else
        {
            desc = "未及格";
        }
        return name + desc;
    }
    

    用表达式树表示如下:

    private static void Test()
    {
    	//定义三个参数: string name,ing score,int standard
        ParameterExpression paraName = Expression.Parameter(typeof(string), "name");
        ParameterExpression paraScore = Expression.Parameter(typeof(int), "score");
        ParameterExpression paraStandard = Expression.Parameter(typeof(int), "standard");
        //定义一个局部变量:string desc;
        ParameterExpression defineDesc = Expression.Parameter(typeof(string), "desc");
        //给局部变量赋值:desc="";
        BinaryExpression assignDesc = Expression.Assign(defineDesc, Expression.Constant(""));
        //准备赋值语句:desc="已及格"
        BinaryExpression assignDescYijige = Expression.Assign(defineDesc, Expression.Constant("已及格"));
        //准备赋值语句:desc="刚及格"
        BinaryExpression assignDescGangjige = Expression.Assign(defineDesc, Expression.Constant("刚及格"));
        //准备赋值语句:desc="未及格"
        BinaryExpression assignDescWeijige = Expression.Assign(defineDesc, Expression.Constant("未及格"));
        //准备代码:score>standard
        BinaryExpression greaterThan = Expression.MakeBinary(ExpressionType.GreaterThan, paraScore, paraStandard);
        //准备代码:score==standard
        BinaryExpression equalThan = Expression.MakeBinary(ExpressionType.Equal, paraScore, paraStandard);
        //准备代码:score<standard
        BinaryExpression lessThan = Expression.MakeBinary(ExpressionType.LessThan, paraScore, paraStandard);
        //组装判断逻辑块:if(score>standard){desc="已及格"}else{if(score==standard){desc="刚及格"}else{desc="未及格"}}
        ConditionalExpression conditional = Expression.Condition(greaterThan, assignDescYijige, Expression.Condition(equalThan, assignDescGangjige, assignDescWeijige));
        //准备代码:name+desc (注意:methodof运算符是用c#自定义的,代码在后面)
        BinaryExpression addAssign = Expression.Add(paraName, defineDesc, (methodof<Func<string, string, string>>)(String.Concat));
        //定义标记:这个标记用于方法返回值
        LabelTarget labelTarget = Expression.Label(typeof(string));
        //定义返回代码段:默认返回 desc
        LabelExpression labelExpression = Expression.Label(labelTarget, defineDesc);
        //定义return语句(这里是goto语句):goto labelTarget 
        GotoExpression gotoExpression = Expression.Return(labelTarget, addAssign, typeof(string));
        //组装这个方法的方法体(指定局部变量)
        BlockExpression block = Expression.Block(typeof(string), new ParameterExpression[] { defineDesc }, assignDesc, conditional, gotoExpression, labelExpression);
        //完成组装这个方法(指定参数)
        LambdaExpression expression = Expression.Lambda<Func<string, int, int, string>>(block, new ParameterExpression[] { paraName, paraScore, paraStandard });
    
    	//编译测试...
        var delega = expression.Compile();
        var func = delega as Func<string, int, int, string>;
        var res = func("xiaoming", 5, 6);
        Console.WriteLine($"func(\"xiaoming\", 5, 6)=>{func("xiaoming", 5, 6)}");
        Console.WriteLine($"func(\"xiaoming\", 2, 6)=>{func("xiaoming", 2, 6)}");
        Console.WriteLine($"func(\"xiaoming\", 6, 6)=>{func("xiaoming", 6, 6)}");
        Console.WriteLine($"func(\"xiaoming\", 8, 6)=>{func("xiaoming", 8, 6)}");
    	
    	//尝试打印出表达式树的字符串形式    
        Console.WriteLine("整体输出...");
        Console.WriteLine(expression.ToString());
        Console.WriteLine("--------------------------start");
        Console.WriteLine(assignDesc.ToString());
        Console.WriteLine(conditional);
        Console.WriteLine(gotoExpression);
        Console.WriteLine(labelExpression);
        Console.WriteLine("---------------------------end");
    }
    

    上面用到的methodof运算符定义如下:

    public class methodof<T>
    {
        private MethodInfo method;
    
        public methodof(T func)
        {
            Delegate del = (Delegate)(object)func;
            this.method = del.Method;
        }
    
        public static implicit operator methodof<T>(T methodof)
        {
            return new methodof<T>(methodof);
        }
    
        public static implicit operator MethodInfo(methodof<T> methodof)
        {
            return methodof.method;
        }
    }
    

    整个代码输出如下:
    在这里插入图片描述
    从上面可以看到表达式树不仅可以组装简单的lambda表达式,还可以带语句块的复杂方法。

    3.2 使用编译器的语法糖

    同样是上面的lambda表达式`:(x,y)=>x+y,代码如下:

    static void Main(string[] args)
    {
        //组装表达式树
        Expression<Func<int, int, int>> expression = (x, y) => x + y;
        //编译并调用
        var res = expression.Compile()(1, 2);
        Console.WriteLine($"表达式树的执行结果:(1,2)=>{res}");
        Console.WriteLine("Hello World!");
        Console.ReadLine();
    }
    

    毫无疑问,运行效果是一样的:
    在这里插入图片描述
    我们用.Net Reflector反编译看一下:
    在这里插入图片描述
    可以看到,这就是个语法糖。我们在entity framework中经常会用到这种语法糖,看下图:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    从图中可以看到,entity framework中的参数传递用的都是表达式树(而不是我们熟悉的委托。。。),但一般我们穿进去的是lambda表达式,这里也是语法糖:编译器会在编译的时候将你写的委托组装成表达式树再传进入:
    在这里插入图片描述
    注意:
    这个语法糖有一个限制:就是你的lambda代码不能有代码块(而这个特点正好被用在了entity framework上防止你任意的输入无限量代码)!
    看下图:
    在这里插入图片描述

    四、表达式树能做什么?

    上面讲到了,手动组装表达式树然后编译调用,现实中肯定不能这么用,如果是自己组装表达式再自己调用的话,为什么不干脆直接调用委托?
    所以说,表达式树是给框架的作者用的。试想一下:当你调用框架方法的时候,你会传入一个简单的lambda表达式(lambda表达式还是很方便的),然后框架拿到你传入的表达式树(编译器编译时就已经将你的lambda表达式改装成了表达式树)并进行分析结构,然后做一些东西(对于entity framework来说就是分析表达式树 -> 生成适当sql语句->执行sql语句)。

    未完待续。。。

    展开全文
  • 一、前言 一直想写一篇Dpper的定制化扩展的文章,但是里面会设计到对Lambda表达式的解析,而解析Lambda表达式,就必须要知道表达式树的相关知识点。...C# 表达式树讲解(一) C# 表达式树遍历(二) C#...
  • C# - LINQ 表达式树

    2018-07-02 17:34:00
    表达式树(Expression Tree)...对表达式树的代码进行编辑修改,使表达式树中的代码变成动态代码,根据不同的数据库修改树上的代码逻辑从而达到动态切换数据库查询语句的目的,用表达式树可以动态构建针对不同数...
  • C# 表达式树讲解(一) C# 表达式树遍历(二) C# 表达式树分页扩展(三) C# 表达式树Lambda扩展(四) 二、表达式树的遍历 要查看表达式树的遍历,肯定不能直接用.Net Framework封装的方法,因为....
  • 因为对表达式树有点兴趣,出于练手的目的,试着写了一个深拷贝的工具库。支持.net standard2.0或.net framework4.5及以上。 GitHub地址https://github.com/blurhkh/DeepCopier nuget地址...
  • 它可以包含表达式和语句,并且可用于创建委托或表达式目录树类型,支持带有可绑定到委托或表达式树的输入参数的内联表达式。所有Lambda表达式都使用Lambda运算符=>,该运算符读作"goes to"。Lambda运算符的左边是...
  • class Test { static void Main(string[] args) { //c#Expression表达式树了解 Expression<Func<int>> add = () => 1 + 2; var func = add.Co...
  • 表达式树 以前没听过表达式树,只听过表达式,而且听过Lambda表达式,首先介绍一下.NET里表达式树的核心概念:讲代码作为数据,他将一些代码表示为一个对象树,树中的每个节点本身都是一个表达式,不同的表达式类型代表能...
  • linq就是表达式树的最重要价值的体现 因为可以通过 lambda表达式,直接转换为 expression tree所以,通过linq所构造的连式lambda就可以构造任意复杂的算法数据结构 而对这个算法数据结构的,解析,和翻译将可以...
  • 表达式树总结 基础 表达式树提供了一个将可执行代码转换成数据的方法.如果你要在执行代码之前修改或转换此代码,那么它是很有用的.有其是当你要将C#代码----如LINQ查询表达式转换成其他代码在另一个程序----如SQL...
  • C#_表达式目录的应用  使用表达式目录实现两个不同类型的属性赋值:   People类: 1 public class People 2 { 3 public int Age { get; set; } 4 public string Name { get; set; } 5 6 ....
  • 表达式目录,在C#中用Expression标识,这里就不介绍表达式目录是什么了,有兴趣可以自行百度搜索,网上资料还是很多的。 这里主要分享的是如何动态构建表达式目录。 构建表达式目录的代码挺简单的,但是...
  • Expression就是表达式目录,是以形数据结构表示代码,其中每一个节点都是一种表达式。 用lambda表达式来创建一个简单的Expression 使用lambda表达式,编译器在生成IL时会帮我们拼装表达式目录,示例: ...
  • Expression表达式树缓存 Expression表达式树序列化
  • 以下使用扩展方式实现 /// <summary>... /// 表达式树运算 扩展方法 /// </summary> public static class ExpressionExtension { /// <summary> /// 并 expr1 AND expr2 //...
  • C# Expression表达式树的And、Or逻辑运算(可用于Lambda表达式构建条件) PredicateBuilder类(linq多条件组合查询) PredicateBuilder类如下: public static class PredicateBuilder { /// <...
1 2 3 4 5 ... 20
收藏数 16,085
精华内容 6,434