博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
List<T>列表通用过滤模块设计
阅读量:5112 次
发布时间:2019-06-13

本文共 10906 字,大约阅读时间需要 36 分钟。

需求描述

数据列表如List<Customer>

加载到DataGridView后,如果记录比较多可能需要对其进行二次过滤,即客户端过滤
过滤条件做成可由用户设置的,如下图:

在数据源是DataTable时,使用DataView的RowFilter可以轻松按用户的配置拼接出过滤表达式字符串来,

设置RowFilter就可以实现过滤效果,但是当数据源是List<T>这样由EF,Linq
to sql 等框架返回的集合时要实现上面的功能就需要费点力气了。

问题分析:

首先参考上面的截图,用户设置好过滤条件后会形成:" (工号 = 222 And 部门=人力) Or 性别=女" 这样的过滤表达式,可以表示成(Exp1

And Exp2) Or Exp3
这样的形式.针对"工号=222"这样的Exp求值我们会转变成针对Employe实体的EmpId属性是否等于222的判断(Employe.EmpId==222),这个可以通过反射方式来实现,将多个Exp求值的结果通过And或Or连接并运算得出最终结果,True表示这一行(Employe)符合.

不过考虑Exp1 Or Exp2 Or

Exp3  这样的条件,如果第一个Exp1是True的话结果必定是True,这个时候还去计算Exp2,Exp3是完全多余的,如果List集合有几万条记录(当然超过几千行的列表对用户来说是没有多少意义的,一般人不会看那么多行,这个时候应该想想过滤条件设置是否合理)那么针对列表的每个实体的每个属性(字段)使用反射的方式计算一遍Exp将是一个比较大的开销,好在And与Or跟算术操作符(+,-,*,/)有所不同,And运算时只要两个操作数中有一个是False就没必要计算另外一个操作数(这里的是Exp)而Or在一个操作数是True时就可以忽略另一个操作数。不过当所的Exp都是false时针对上面"Exp1
Or Exp2 Or
Exp3"这样的表达式计算每个Exp是不可避免的
到这里我们可以看到该问题的本质就是表达式求值,而操作符只限And与Or两个二元操作符,最后结果是True或False.

设计实现:

首先我们将用户设置的过滤表达式转变成逆波兰式(后缀表达式),接着传入每个要判断的实体,使用后缀表达式求出该实体是否符合过滤条件,
当然我们也可以将后缀表达式构建成Expression树,接着将该Expression编译成动态方法(委托),使用该委托对每个实体做出判断,下面的代码给出了这两种实现,
经过测试发现两种方法速度区别不大。

自己对逆波兰式求值时需要下面的判定表,如果构建Expression树则Expression.AndAlso或Expression.OrElse会自己判断是否对两个操作数都进行计算(参考下面的代码)

代码:

View Code
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Linq.Expressions;using System.Reflection;namespace FIStudio.WinUI.Core{    public class ExpParser    {                public ExpParser(List
midfixList) { this.MidfixList = midfixList; } public List
PostfixList { get; private set; } public List
MidfixList { get; private set; } private Stack
CalcStack = new Stack
(); private void GuardMidfixListExist() { if (MidfixList == null || MidfixList.Count <= 0) throw new Exception("中序列表为null或为空!"); } private void EnsurePostfixReady() { if (PostfixList == null) { PostfixList = DoParse(MidfixList); if (PostfixList == null || PostfixList.Count <= 0) throw new Exception("后序列表为null或为空!"); } } ///
/// 判断元素是否符合要求 /// ///
///
public bool IsSatisfy(object ent) { GuardMidfixListExist(); EnsurePostfixReady(); CalcStack.Clear(); foreach (var item in PostfixList) { if (item is ExpElem) { CalcStack.Push(item); continue; } #region And 运算 if (item is AndElem) { var op1 = CalcStack.Pop() as ExpElem; var op2 = CalcStack.Pop() as ExpElem; //任意一个是false则直接压入false if (op1.Result == false || op2.Result == false) { CalcStack.Push(new ExpElem() { Result = false }); continue; } if (!op1.Result.HasValue && !op2.Result.HasValue) { op1.Compare(ent); if (op1.Result.Value == false) { CalcStack.Push(new ExpElem() { Result = false }); continue; } op2.Compare(ent); CalcStack.Push(new ExpElem() { Result = op2.Result }); continue; } if (!op1.Result.HasValue && op2.Result == true) { op1.Compare(ent); CalcStack.Push(new ExpElem() { Result = op1.Result }); continue; } if (op1.Result == true && !op2.Result.HasValue) { op2.Compare(ent); CalcStack.Push(new ExpElem() { Result = op2.Result }); continue; } if (op1.Result == true && op2.Result == true) { CalcStack.Push(new ExpElem() { Result = true }); continue; } } #endregion #region Or 运算 if (item is OrElem) { var op1 = CalcStack.Pop() as ExpElem; var op2 = CalcStack.Pop() as ExpElem; //任意一个是true则直接压入true if (op1.Result == true || op1.Result == true) { CalcStack.Push(new ExpElem() { Result = true }); continue; } if (!op1.Result.HasValue && !op2.Result.HasValue) { op1.Compare(ent); if (!op1.Result == true) { CalcStack.Push(new ExpElem() { Result = true }); continue; } op2.Compare(ent); CalcStack.Push(new ExpElem() { Result = op2.Result }); } if (!op1.Result.HasValue && op2.Result == false) { op1.Compare(ent); CalcStack.Push(new ExpElem() { Result = op1.Result }); continue; } if (op1.Result == false && !op2.Result.HasValue) { op2.Compare(ent); CalcStack.Push(new ExpElem() { Result = op2.Result }); continue; } if (op1.Result == false && op2.Result == false) { CalcStack.Push(new ExpElem() { Result = false }); } } #endregion } return (CalcStack.Pop() as ExpElem).Result.Value; } ///
/// 生成判断函数 /// ///
public Expression
> GenIsSatisfyFunc
() { GuardMidfixListExist(); EnsurePostfixReady(); Stack
stack = new Stack(); ParameterExpression entExp = Expression.Parameter(typeof(T), "ent"); foreach (var elem in PostfixList) { if (elem is ExpElem) { stack.Push(elem); continue; } if (elem is AndElem) { var elem1 = stack.Pop(); var elem2 = stack.Pop(); var exp= Expression.AndAlso(GetCallExpression(elem1,entExp),GetCallExpression(elem2,entExp)); stack.Push(exp); continue; } if(elem is OrElem) { var elem1 = stack.Pop(); var elem2 = stack.Pop(); var exp= Expression.OrElse(GetCallExpression(elem1,entExp),GetCallExpression(elem2,entExp)); stack.Push(exp); continue; } } LambdaExpression lambda= Expression.Lambda
>( stack.Pop() as Expression,entExp); return lambda as Expression
>; } private Expression GetCallExpression(object elem, ParameterExpression entExp) { if (elem is ExpElem) { return Expression.Call(Expression.Constant(elem), typeof(ExpElem).GetMethod("Compare"), entExp); } return elem as Expression; } ///
/// 中序表达式转后缀表达式 /// ///
///
private List
DoParse(List
midfix) { Stack
stack = new Stack
(); var list=new List
(); foreach (var elem in midfix) { if (elem is ExpElem) { list.Add(elem); continue; } if (elem is LBElem) { stack.Push(elem); continue; } if (elem is RBElem) { var e = stack.Pop(); while (!(e is LBElem)) { list.Add(e); e = stack.Pop(); } continue; } if((elem is AndElem) || (elem is OrElem)) { if (stack.Count > 0) { var e = stack.Peek(); while ( !(e is LBElem) && elem.Priority <= e.Priority) { list.Add(stack.Pop()); if (stack.Count <= 0) break; e = stack.Peek(); } } stack.Push(elem); } } while (stack.Count > 0) { list.Add(stack.Pop()); } return list; } } #region 节点定义 public class Elem { public virtual string Name { get; set; } public virtual int Priority { get; set; } public Object Data { get; set; } } ///
/// 左括号 /// 注意stack中只会压入'(','And','Or' /// public class LBElem : Elem { public override string Name { get { return "("; } } public override int Priority { get { return 59; } } } ///
/// 右括号 /// public class RBElem : Elem { public override string Name { get { return ")"; } } public override int Priority { get { return 99; } } } public class AndElem : Elem { public override string Name { get { return "And"; } } public override int Priority { get { return 88; } } } public class OrElem : Elem { public override string Name { get { return "Or"; } } public override int Priority { get { return 77; } } } public class ExpElem : Elem { public override int Priority { get { return 66; } } public bool Compare(object ent) { Console.WriteLine("计算了:" + Name); bool? ret=null; if (AssertType == Core.CompareType.Equal) { ret= string.Compare(GetV(ent),Value,true)==0; } if (AssertType == Core.CompareType.NotEqual) { ret = string.Compare(GetV(ent), Value, true) != 0; } if (AssertType == Core.CompareType.Greate) { ret = string.Compare(GetV(ent), Value, true) > 0; } if (AssertType == Core.CompareType.GreateOrEqual) { ret = string.Compare(GetV(ent), Value, true) >= 0; } if (AssertType == Core.CompareType.Less) { ret = string.Compare(GetV(ent), Value, true) < 0; } if (AssertType == Core.CompareType.LessOrEqual) { ret = string.Compare(GetV(ent), Value, true) <= 0; } if (AssertType == Core.CompareType.Contains) { ret = GetV(ent).Contains(Value); } if (AssertType == Core.CompareType.NoContains) { ret =! GetV(ent).Contains(Value); } if (AssertType == Core.CompareType.StartWith) { ret = GetV(ent).StartsWith(Value); } if (AssertType == Core.CompareType.EndWith) { ret = GetV(ent).EndsWith(Value); } if (!ret.HasValue) throw new Exception("未知的CompareType!"); Result = ret; return ret.Value; } public bool? Result { get; set; } public PropertyInfo Property { get; set; } public CompareType AssertType { get; set; } public string Value { get; set; } private string GetV(object ent) { var tmp= Property.GetValue(ent, null); if (tmp == null) tmp = string.Empty; return tmp.ToString(); } } public enum CompareType { Equal, NotEqual, Less, LessOrEqual, Greate, GreateOrEqual, Contains, NoContains, StartWith, EndWith };#endregion}

参考:

  逆波兰式构建方法

  1、从左至右扫描一中缀表达式。

               
2、若读取的是操作数,则判断该操作数的类型,并将该操作数存入操作数堆栈
               
3、若读取的是运算符
                   (1)
该运算符为左括号"(",则直接存入运算符堆栈。
                   (2)
该运算符为右括号")",则输出运算符堆栈中的运算符到操作数堆栈,直到遇到左括号为止,此时抛弃该左括号。
                   (3)
该运算符为非括号运算符:
                       (a)
若运算符堆栈栈顶的运算符为左括号,则直接存入运算符堆栈。
                       (b)
若比运算符堆栈栈顶的运算符优先级高,则直接存入运算符堆栈。
                       (c)
若比运算符堆栈栈顶的运算符优先级低或相等,则输出栈顶运算符到操作数堆栈,
                           
直至运算符栈栈顶运算符低于(不包括等于)该运算符优先级,或为左括号,并将当前运算符压入运算符堆栈。
               
4、当表达式读取完成后运算符堆栈中尚有运算符时,则依序取出运算符到操作数堆栈,直到运算符堆栈为空。

   逆波兰表达式求值算法:

               1、循环扫描语法单元的项目。

              
2、如果扫描的项目是操作数,则将其压入操作数堆栈,并扫描下一个项目。
              
3、如果扫描的项目是一个二元运算符,则对栈的顶上两个操作数执行该运算。
              
4、如果扫描的项目是一个一元运算符,则对栈的最顶上操作数执行该运算。
              
5、将运算结果重新压入堆栈。
               6、重复步骤2-5,堆栈中即为结果值。

  资源

转载于:https://www.cnblogs.com/lhj588/archive/2012/10/02/2710242.html

你可能感兴趣的文章
javascript小技巧
查看>>
C#登录窗口(访问数据库)的制作,类文件的制作及使用
查看>>
大小写转换
查看>>
[树状数组][二分] 洛谷 P2161 会场预约
查看>>
[数位dp] Jzoj P4239 光棍
查看>>
167. Two Sum II - Input array is sorted两数之和
查看>>
面试中关于Java你所需知道的的一切
查看>>
由一个activity跳转到另一个activity
查看>>
PLSQL
查看>>
浮动float的一些规则
查看>>
跳频通信(梅文华)pdf
查看>>
Java基础-重要版本
查看>>
POJ1006 中国剩余定理
查看>>
部署JUnit
查看>>
【图论 搜索】bzoj1064: [Noi2008]假面舞会
查看>>
Python补充之函数
查看>>
获取含有class为某个值的a标签或img标签
查看>>
接口测试概念
查看>>
【LeetCode】107. Binary Tree Level Order Traversal II (2 solutions)
查看>>
【LeetCode】124. Binary Tree Maximum Path Sum
查看>>