C#表达式目录树示例详解

论坛 期权论坛 脚本     
niminba   2021-5-23 02:57   1281   0

1、表达式目录树

  表达式目录树,在C#中是Expression来定义的,它是一种语法树,或者说是一种数据结构。其主要用于存储需要计算、运算的一种结构,它只提供存储功能,不进行运算。通常Expression是配合Lambda一起使用,lambda可以是匿名方法。Expression可以动态创建。

  声明一个lambda表达式,其中可以指明类型,也可以是匿名方法:

//Func<int, int, int> func = new Func<int, int, int>((m, n) => m * n + 2);
Func<int, int, int> func = (m, n) => m * n + 2;

  上述代码可以使用Expression来定义:

Expression<Func<int, int, int>> exp = (m, n) => m * n + 2;//lambda表达式声明表达式目录树

  Expression的方法体只能是一个整体,不能具有花括号,以下代码是不允许的:

Expression<Func<int, int, int>> exp1 = (m, n) =>//方法体只能一体
{
 return m * n + 2;
};

  上述func和exp执行结果相同:

int iResult1 = func.Invoke(3, 2);
int iResult2 = exp.Compile().Invoke(3, 2);

2、构建表达式目录树

  上述表达式示例可以通过Expression来自主构建,把m、n定义为ParameterExpression参数,把2定义为常数表达式ConstantExpression,使用Expression的静态方法,表示乘和加:

ParameterExpression parameterLeft = Expression.Parameter(typeof(int), "m");//定义参数
ParameterExpression parameterRight = Expression.Parameter(typeof(int), "n");//定义参数
BinaryExpression binaryMultiply = Expression.Multiply(parameterLeft, parameterRight);//组建第一步的乘法  
ConstantExpression constant = Expression.Constant(2, typeof(int)); //定义常数参数
BinaryExpression binaryAdd = Expression.Add(binaryMultiply, constant);//组建第二步的加法
var expression = Expression.Lambda<Func<int, int, int>>(binaryAdd, parameterLeft, parameterRight);//构建表达式 
var func = expression.Compile(); //编译为lambda表达式
int iResult3 = func(3, 2);
int iResult4 = expression.Compile().Invoke(3, 2);
int iResult5 = expression.Compile()(3, 2);

  自主构建Expression是,参数名称的定义,可以不是m、n,可以是其他的a、b或者x、y。

  如何构建一个复杂的表达式目录树?需要使用到Expression中更多的方法、属性、扩展方法等。首先定义一个类:

public class People
{
 public int Age { get; set; }
 public string Name { get; set; }
 public int Id;
}

  基于上面的类,构建表达式: Expression<Func<People, bool>> lambda = x => x.Id.ToString().Equals("5");

  这个示例中,使用到了int自身的ToString()方法,还使用到了字符串的Equals方法。构建过程如下:

//以下表达式目录树实现lambda的表达式
Expression<Func<People, bool>> lambda = x => x.Id.ToString().Equals("5");
//声明一个参数对象
ParameterExpression parameterExpression = Expression.Parameter(typeof(People), "x");
//查找字段, 并绑定访问参数对象字段(属性)的方法:x.Id
MemberExpression member = Expression.Field(parameterExpression, typeof(People).GetField("Id"));
//以上可以用这个代替
var temp =Expression.PropertyOrField(parameterExpression, "Id");
//调用字段的ToString方法:x.Id.ToString()
MethodCallExpression method = Expression.Call(member, typeof(int).GetMethod("ToString", new Type[] { }), new Expression[0]);
//调用字符串的Equals方法:x.Id.ToString().Equals("5")
MethodCallExpression methodEquals = Expression.Call(method, typeof(string).GetMethod("Equals", new Type[] { typeof(string) }), new Expression[]
{
 Expression.Constant("5", typeof(string))//与常量进行比较,也可以是参数
});
//创建目录树表达式
ar expression = Expression.Lambda<Func<People, bool>>(methodEquals, new ParameterExpression[] {parameterExpression });
bool bResult = expression.Compile().Invoke(new People()
{
 Id = 5,
 Name = "Nigle",
 Age = 31
});

3、使用Expression来进行不同对象的相同名字的属性映射

  前面构建了类People,现在我们构建一个新的类PeopleCopy:

public class PeopleCopy
{
 public int Age { get; set; }
 public string Name { get; set; }
 public int Id;
}

  现在声明一个People对象,然后对People对象的数据进行拷贝到PeopleCopy新对象中去,直接硬编码的方式:

  1. 硬编码

People people = new People()
{
 Id = 11,
 Name = "Nigle",
 Age = 31
};
PeopleCopy peopleCopy = new PeopleCopy()
{
 Id = people.Id,
 Name = people.Name,
 Age = people.Age
}8pressionVisitor visitor = new NewExpressionVisitor(newParameter);
 //需要先将参数替换为一致的,可能参数名不一样
 var left = visitor.Replace(expLeft.Body);//左侧的表达式
 var right = visitor.Replace(expRight.Body);//右侧的表达式
 var body = Expression.And(left, right);//合并表达式
 return Expression.Lambda<Func<T, bool>>(body, newParameter);
 }
 /// <summary>合并表达式 expr1 or expr2</summary>
 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>
 class NewExpressionVisitor : ExpressionVisitor
 {
 public ParameterExpression _NewParameter { get; private set; }
 public NewExpressionVisitor(ParameterExpression param)
 {
  this._NewParameter = param;//用于把参数替换了
 }
 /// <summary> 替换</summary>
 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 = p => p.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();

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> lst1 = people.Where(lambda3.Compile()).ToList();
List<People> lst2 = people.Where(lambda4.Compile()).ToList();
List<People> lst3 = people.Where(lambda5.Compile()).ToList();

总结

到此这篇关于C#表达式目录树的文章就介绍到这了,更多相关C#表达式目录树内容请搜索社区以前的文章或继续浏览下面的相关文章希望大家以后多多支持社区!

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:1060120
帖子:212021
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP