千家信息网

C#中如何实现表达式目录树Expression

发表于:2025-02-08 作者:千家信息网编辑
千家信息网最后更新 2025年02月08日,这篇文章给大家分享的是有关C#中如何实现表达式目录树Expression的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。表达式目录树表达式目录树:语法树,或者说是一种数据结构
千家信息网最后更新 2025年02月08日C#中如何实现表达式目录树Expression

这篇文章给大家分享的是有关C#中如何实现表达式目录树Expression的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。

表达式目录树

表达式目录树:语法树,或者说是一种数据结构
1.表达式目录树Expression:System.Linq.Expressions;
2.描述了多个变量或者和常量之间的关系,按照一定的规则进行组装!

  • 可以向委托一样使用lambd表达式快捷声明;

  • 不能有语句体,声明只能有一行代码;

  • 可以通过Compile(),编译成一个委托;

Func func = (m, n) =>{    int i = 0;    return m * n + 2;};  //委托  拉姆达表达式其实是作为委托的一个参数,本质是一个方法(匿名方法)Expression> exp = (m, n) => m * n + 2; //数据结构--就像对一个计算做了一个精确的描述,展开之后发现,分为左边,右边,每个元素都可以把值都获取出来,二叉树var erpPlu= exp.Compile();//表达式目录树可以通过compile 转换成一个委托//表达式目录树:语法树,或者说是一种数据结构int iResult1 = func.Invoke(12, 23);int iResult2 = exp.Compile().Invoke(12, 23);

表达式目录树的拼装

手动拼装表达式目录树,不是用的lambda的快捷方式

//表达式目录树的拼装Expression> expression = () => 123 + 234;  //两个常量相加-----表达式目录树的快捷声明Expression constant123 = Expression.Constant(123);Expression constant234 = Expression.Constant(234);Expression expressionAdd = Expression.Add(constant123, constant234);var exp = Expression.Lambda>(expressionAdd);var func = exp.Compile();int iResult = func.Invoke();
Expression> exp = (m, n) => m * n + m + n + 2; //快捷声明--其实编译器提供的便捷功能---语法糖--具体实现可通过反编译工具查看//具体实现可通过反编译工具查看ParameterExpression parameterExpression = Expression.Parameter(typeof(int), "m");ParameterExpression parameterExpression2 = Expression.Parameter(typeof(int), "n");Expression expContant2 = Expression.Constant(2, typeof(int));Expression multipley = Expression.Multiply(parameterExpression, parameterExpression2);Expression expAdd = Expression.Add(multipley, parameterExpression);Expression expAdd1 = Expression.Add(expAdd, parameterExpression2);Expression expAdd2 = Expression.Add(expAdd1, expContant2);Expression> expression = Expression.Lambda>(expAdd2, new ParameterExpression[]{parameterExpression,parameterExpression2});Func fun = expression.Compile();int iResult = fun.Invoke(10, 11);
var peopleQuery = new List().AsQueryable();Expression> lambda = x => x.Id.ToString().Equals("5");peopleQuery.Where(lambda);ParameterExpression parameterExpression = Expression.Parameter(typeof(People), "x");FieldInfo idfield = typeof(People).GetField("Id");var idExp = Expression.Field(parameterExpression, idfield);MethodInfo toString = typeof(int).GetMethod("ToString", new Type[0]);var toStringExp = Expression.Call(idExp, toString, Array.Empty()); var Equals = typeof(string).GetMethod("Equals", new Type[] { typeof(string) });Expression expressionConstant5 = Expression.Constant("5", typeof(string));var equalsExp = Expression.Call(toStringExp, Equals, new Expression[] {    expressionConstant5 });Expression> expression = Expression.Lambda>(equalsExp, new ParameterExpression[]{ parameterExpression});Func func = expression.Compile();var bResult = func.Invoke(new People(){    Id = 5,    Name = "海贝"});new List().AsQueryable().Where(expression);

应用

Linq to SQL

var dbSet = new List().AsQueryable();//EF DbSet dbSet.Where(p => p.Age == 25 & p.Name.Contains("阳光下的微笑"));Expression> exp = null;Console.WriteLine("用户输入个名称,为空就跳过");string name = Console.ReadLine();if (!string.IsNullOrWhiteSpace(name)){    exp = p => p.Name.Contains(name);}Console.WriteLine("用户输入个最小年纪,为空就跳过");string age = Console.ReadLine();if (!string.IsNullOrWhiteSpace(age) && int.TryParse(age, out int iAge)){    exp = p => p.Age > iAge;}

上面的玩法是不是只有最后一个条件才生效?如果需要多个条件都满足;怎么办? 当然是拼装啊;
拼装可以从最小粒度来组装表达式目录树;如果有一个封装,你把各种条件给我,我从最小粒度开始一个一个的拼装起来,不就是一个长的表达式目录树了吗?

解决方案:
调用方可以组装一个很长的表达式目录树传递过来;
表达式目录树传递过来以后,在这里应该做什么?应该解析;
所有信息都在表达式目录树里面,自然也可以把他解析(找出来)
解析就可以通过ExpressionVisitor解析----生成对应的Sql语句;

ExpressionVisitor

表达式目录树的访问者----访问者模式;
1.Visit方法-访问表达式目录树的入口-分辨是什么类型的表达式目录
2.调度到更加专业的方法中进一步访问,访问一遍之后,生成一个新的表达式目录 -有点像递归,不全是递归;
3.因为表达式目录树是个二叉树,ExpressionVisitor一直往下访问,一直到叶节点;那就访问了所有的节点;
4.在访问的任何一个环节,都可以拿到对应当前环节的内容(参数名称、参数值。。),就可以进一步扩展;

为什么要使用表达式目录树来拼装解析呢:
1.可以提高重用性;
2.如果封装好一个方法,接受一个表达式目录树,在解析的时候,其实就是不断的访问,访问有规则;
3.任何一个表达式目录树都可以调用当前方法来解析;
4.表达式目录树可以支持泛型;

{                Expression> lambda = x => x.Age > 5 && x.Id > 5                                                         && x.Name.StartsWith("1") //  like '1%'                                                         && x.Name.EndsWith("1") //  like '%1'                                                         && x.Name.Contains("1");//  like '%1%'                //string sql = string.Format("Delete From [{0}] WHERE [Age]>5 AND [ID] >5"                    , typeof(People).Name                    , " [Age]>5 AND [ID] >5" );                 ConditionBuilderVisitor vistor = new ConditionBuilderVisitor();                vistor.Visit(lambda);                Console.WriteLine(vistor.Condition());            }            {                // ((( [Age] > '5') AND( [Name] =  [name] )) OR( [Id] > '5' ))                string name = "AAA";                 Expression> lambda = x => x.Age > 5 && x.Name == name || x.Id > 5;                ConditionBuilderVisitor vistor = new ConditionBuilderVisitor();                vistor.Visit(lambda);                Console.WriteLine(vistor.Condition());            }            {                Expression> lambda = x => x.Age > 5 || (x.Name == "A" && x.Id > 5);                ConditionBuilderVisitor vistor = new ConditionBuilderVisitor();                vistor.Visit(lambda);                Console.WriteLine(vistor.Condition());            }            {                Expression> lambda = x => (x.Age > 5 || x.Name == "A") && x.Id > 5;                ConditionBuilderVisitor vistor = new ConditionBuilderVisitor();                vistor.Visit(lambda);                Console.WriteLine(vistor.Condition());            }

自己封装的解析器,这就是EF6的底层原理,根据表达式树自动生成相应的sql语句。

public class ConditionBuilderVisitor : ExpressionVisitor    {        private Stack _StringStack = new Stack();        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;             if (node.Expression is ConstantExpression)            {                var value1 = this.InvokeValue(node);                var value2 = this.ReflectionValue(node);                //this.ConditionStack.Push($"'{value1}'");                this._StringStack.Push("'" + value2 + "'");            }            else            {                this._StringStack.Push(" [" + node.Member.Name + "] ");            }            return node;        }        private object InvokeValue(MemberExpression member)        {            var objExp = Expression.Convert(member, typeof(object));//struct需要            return Expression.Lambda>(objExp).Compile().Invoke();        }        private object ReflectionValue(MemberExpression member)        {            var obj = (member.Expression as ConstantExpression).Value;            return (member.Member as FieldInfo).GetValue(obj);        }        ///         /// 常量表达式        ///         ///         ///         protected override Expression VisitConstant(ConstantExpression node)        {            if (node == null) throw new ArgumentNullException("ConstantExpression");            this._StringStack.Push(" '" + node.Value + "' ");            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;        }    }
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("不支持该方法");            }        }    }

表达式目录扩展

表达式目录树动态拼接的实现方式:

///     /// 合并表达式 And Or  Not扩展    ///     public static class ExpressionExtend    {        ///         /// 合并表达式 expr1 AND expr2        ///         ///         ///         ///         ///         public static Expression> And(this Expression> expr1, Expression> expr2)        {            //return Expression.Lambda>(Expression.AndAlso(expr1.Body, expr2.Body), expr1.Parameters); 错误的写法,两个表达式不是同一个参数                        //将两个表达式的参数统一为参数c            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);        }        ///         /// 合并表达式 expr1 or expr2        ///         ///         ///         ///         ///         public static Expression> Or(this Expression> 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);        }    }
///     /// 建立新表达式    ///     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);        }        protected override Expression VisitParameter(ParameterExpression node)        {            return this._NewParameter;        }    }

调用方如下:

{                Expression> lambda1 = x => x.Age > 5;                  Expression> lambda2 = x => x.Id > 5;                //Expression> newExpress = x => x.Age > 5 && x.Id > 5;                Expression> lambda3 = lambda1.And(lambda2); //且                Expression> lambda4 = lambda1.Or(lambda2);//或                Expression> lambda5 = lambda1.Not();//非                Do1(lambda3);                Do1(lambda4);                Do1(lambda5);            }                private static void Do1(Expression> func)        {            List people = new List()            {                new People(){Id=4,Name="123",Age=4},                new People(){Id=5,Name="234",Age=5},                new People(){Id=6,Name="345",Age=6},            };            List peopleList = people.Where(func.Compile()).ToList();        }

对象深拷贝

硬编码

 PeopleCopy peopleCopy = new PeopleCopy() {     Id = people.Id,     Name = people.Name,     Age = people.Age };

通过反射实现

public class ReflectionMapper    {        ///         /// 反射        ///         ///         ///         ///         ///         public static TOut Trans(TIn tIn)        {            TOut tOut = Activator.CreateInstance();            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;        }    }

通过序列化实现

///     /// 使用第三方序列化反序列化工具    /// 还有automapper    ///     public class SerializeMapper    {        ///         /// 序列化反序列化方式        ///         ///         ///         public static TOut Trans(TIn tIn)        {            return JsonConvert.DeserializeObject(JsonConvert.SerializeObject(tIn));        }    }

反射和序列化两种实现方式性能不太好;

通过表达式目录树实现

通过表达式目录树动态的生成硬编码

Func func = p => new PeopleCopy(){    Id = p.Id,    Name = p.Name,    Age = p.Age};PeopleCopy peopleCopy3 = func.Invoke(people);

方法一:普通缓存

///     /// 生成表达式目录树 缓存    ///     public class ExpressionMapper    {        ///         /// 字典缓存--hash分布        ///         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 property = Expression.Field(parameterExpression, typeof(TIn).GetField(item.Name));                    MemberBinding memberBinding = Expression.Bind(item, property);                    memberBindingList.Add(memberBinding);                }                MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());                Expression> lambda = Expression.Lambda>(memberInitExpression, new ParameterExpression[]                {                    parameterExpression                });                Func func = lambda.Compile();//拼装是一次性的                _Dic[key] = func;            }            return ((Func)_Dic[key]).Invoke(tIn);        }    }

方法二:泛型缓存,性能较高

///     /// 生成表达式目录树  泛型缓存    ///     ///     ///     public class ExpressionGenericMapper//Mapper`2    {        private static Func _FUNC = null;        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 property = Expression.Field(parameterExpression, typeof(TIn).GetField(item.Name));                MemberBinding memberBinding = Expression.Bind(item, property);                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);        }    }

感谢各位的阅读!关于"C#中如何实现表达式目录树Expression"这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!

0