C# 表达式目录树Expression的实现

作者:如果我来了6 时间:2023-04-03 22:57:32 

表达式目录树

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

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

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

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


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

//表达式目录树:语法树,或者说是一种数据结构
int iResult1 = func.Invoke(12, 23);
int iResult2 = exp.Compile().Invoke(12, 23);

C# 表达式目录树Expression的实现

C# 表达式目录树Expression的实现

表达式目录树的拼装

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


//表达式目录树的拼装
Expression<Func<int>> expression = () => 123 + 234;  //两个常量相加-----表达式目录树的快捷声明

Expression constant123 = Expression.Constant(123);
Expression constant234 = Expression.Constant(234);
Expression expressionAdd = Expression.Add(constant123, constant234);
var exp = Expression.Lambda<Func<int>>(expressionAdd);
var func = exp.Compile();
int iResult = func.Invoke();

Expression<Func<int, int, int>> 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<Func<int, int, int>> expression = Expression.Lambda<Func<int, int, int>>(expAdd2, new ParameterExpression[]
{
parameterExpression,
parameterExpression2
});
Func<int, int, int> fun = expression.Compile();
int iResult = fun.Invoke(10, 11);

var peopleQuery = new List<People>().AsQueryable();
Expression<Func<People, bool>> 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<Expression>());
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<Func<People, bool>> expression = Expression.Lambda<Func<People, bool>>(equalsExp, new ParameterExpression[]
{
parameterExpression
});
Func<People, bool> func = expression.Compile();
var bResult = func.Invoke(new People()
{
   Id = 5,
   Name = "海贝"
});

new List<People>().AsQueryable().Where(expression);

应用

Linq to SQL


var dbSet = new List<People>().AsQueryable();//EF DbSet
dbSet.Where(p => p.Age == 25 & p.Name.Contains("阳光下的微笑"));

Expression<Func<People, bool>> 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<Func<People, bool>> 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<Func<People, bool>> lambda = x => x.Age > 5 && x.Name == name || x.Id > 5;
               ConditionBuilderVisitor vistor = new ConditionBuilderVisitor();
               vistor.Visit(lambda);
               Console.WriteLine(vistor.Condition());
           }
           {
               Expression<Func<People, bool>> lambda = x => x.Age > 5 || (x.Name == "A" && x.Id > 5);
               ConditionBuilderVisitor vistor = new ConditionBuilderVisitor();
               vistor.Visit(lambda);
               Console.WriteLine(vistor.Condition());
           }
           {
               Expression<Func<People, bool>> 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<string> _StringStack = new Stack<string>();

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

/// <summary>
       /// 如果是二元表达式
       /// </summary>
       /// <param name="node"></param>
       /// <returns></returns>
       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;
       }

/// <summary>
       /// 解析属性
       /// </summary>
       /// <param name="node"></param>
       /// <returns></returns>
       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<Func<object>>(objExp).Compile().Invoke();
       }

private object ReflectionValue(MemberExpression member)
       {
           var obj = (member.Expression as ConstantExpression).Value;
           return (member.Member as FieldInfo).GetValue(obj);
       }

/// <summary>
       /// 常量表达式
       /// </summary>
       /// <param name="node"></param>
       /// <returns></returns>
       protected override Expression VisitConstant(ConstantExpression node)
       {
           if (node == null) throw new ArgumentNullException("ConstantExpression");
           this._StringStack.Push(" '" + node.Value + "' ");
           return node;
       }
       /// <summary>
       /// 方法表达式
       /// </summary>
       /// <param name="m"></param>
       /// <returns></returns>
       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("不支持该方法");
           }

}
   }

表达式目录扩展

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


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

}
       /// <summary>
       /// 合并表达式 expr1 or expr2
       /// </summary>
       /// <typeparam name="T"></typeparam>
       /// <param name="expr1"></param>
       /// <param name="expr2"></param>
       /// <returns></returns>
       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);
       }
   }

   /// <summary>
   /// 建立新表达式
   /// </summary>
   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<Func<People, bool>> lambda1 = x => x.Age > 5;  
               Expression<Func<People, bool>> lambda2 = x => x.Id > 5;

//Expression<Func<People, bool>> newExpress = x => x.Age > 5 && x.Id > 5;

Expression<Func<People, bool>> lambda3 = lambda1.And(lambda2); //且
               Expression<Func<People, bool>> lambda4 = lambda1.Or(lambda2);//或
               Expression<Func<People, bool>> lambda5 = lambda1.Not();//非
               Do1(lambda3);
               Do1(lambda4);
               Do1(lambda5);
           }

private static void Do1(Expression<Func<People, bool>> func)
       {
           List<People> people = new List<People>()
           {
               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<People> peopleList = people.Where(func.Compile()).ToList();
       }

对象深拷贝

硬编码


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

通过反射实现


   public class ReflectionMapper
   {
       /// <summary>
       /// 反射
       /// </summary>
       /// <typeparam name="TIn"></typeparam>
       /// <typeparam name="TOut"></typeparam>
       /// <param name="tIn"></param>
       /// <returns></returns>
       public static TOut Trans<TIn, TOut>(TIn tIn)
       {
           TOut tOut = Activator.CreateInstance<TOut>();
           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>
   /// 使用第三方序列化反序列化工具
   /// 还有automapper
   /// </summary>
   public class SerializeMapper
   {
       /// <summary>
       /// 序列化反序列化方式
       /// </summary>
       /// <typeparam name="TIn"></typeparam>
       /// <typeparam name="TOut"></typeparam>
       public static TOut Trans<TIn, TOut>(TIn tIn)
       {
           return JsonConvert.DeserializeObject<TOut>(JsonConvert.SerializeObject(tIn));
       }
   }

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

通过表达式目录树实现

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


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

方法一:普通缓存


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

/// <summary>
       /// 字典缓存表达式树
       /// </summary>
       /// <typeparam name="TIn"></typeparam>
       /// <typeparam name="TOut"></typeparam>
       /// <param name="tIn"></param>
       /// <returns></returns>
       public static TOut Trans<TIn, TOut>(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<MemberBinding> memberBindingList = new List<MemberBinding>();
               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<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[]
               {
                   parameterExpression
               });
               Func<TIn, TOut> func = lambda.Compile();//拼装是一次性的
               _Dic[key] = func;
           }
           return ((Func<TIn, TOut>)_Dic[key]).Invoke(tIn);
       }
   }

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


   /// <summary>
   /// 生成表达式目录树  泛型缓存
   /// </summary>
   /// <typeparam name="TIn"></typeparam>
   /// <typeparam name="TOut"></typeparam>
   public class ExpressionGenericMapper<TIn, TOut>//Mapper`2
   {
       private static Func<TIn, TOut> _FUNC = null;
       static ExpressionGenericMapper()
       {
           ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p");
           List<MemberBinding> memberBindingList = new List<MemberBinding>();
           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<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[]
           {
                   parameterExpression
           });
           _FUNC = lambda.Compile();//拼装是一次性的
       }
       public static TOut Trans(TIn t)
       {
           return _FUNC(t);
       }
   }

来源:https://blog.csdn.net/qq_29821795/article/details/120423477

标签:C#,表达式目录树,Expression
0
投稿

猜你喜欢

  • 零基础入门SpringMVC拦截器的配置与使用

    2023-07-17 21:59:28
  • C#面向对象特征的具体实现及作用详解

    2023-11-14 06:43:39
  • IDEA怎么生成UML类图的实现

    2023-12-07 15:40:21
  • Android提高之使用NDK把彩图转换灰度图的方法

    2023-07-22 02:00:07
  • 聊聊Controller中RequestMapping的作用

    2021-12-08 20:48:45
  • C#操作ftp类完整实例

    2021-09-02 22:45:30
  • java中类加载与双亲委派机制详解

    2023-12-07 06:34:53
  • 简单工厂模式_动力节点Java学院整理

    2022-07-22 16:42:08
  • JavaWeb实现多文件上传及zip打包下载

    2023-11-16 16:41:25
  • Android性能优化之plt hook与native线程监控详解

    2021-09-10 22:06:01
  • Android同时安装Release和Debug版本的方法

    2022-02-11 08:51:20
  • C#时间格式转换为时间戳的方法步骤

    2022-04-13 12:27:19
  • C#词法分析器之词法分析的使用详解

    2022-01-26 04:22:13
  • Java开发人员最常犯的10个错误

    2021-10-25 16:40:08
  • 基于Android 实现图片平移、缩放、旋转同时进行

    2023-05-30 08:30:51
  • Android Studio3安装图文教程

    2022-03-21 04:26:44
  • Springboot2.1.6集成activiti7出现登录验证的实现

    2022-08-03 10:44:48
  • Mybatis中Mapper映射文件使用详解

    2023-02-13 17:57:07
  • Spring事务管理配置文件问题排查

    2022-07-04 15:55:25
  • java HashMap通过value反查key的代码示例

    2022-06-07 08:50:55
  • asp之家 软件编程 m.aspxhome.com