当想对集合筛选的时候,经常想到用Where过滤,而实际上List<T>.FindAll()也是不错的选择。
如果有一个订单,属性有下单时间、区域等等。如何使用List<T>.FindAll(),根据年份、月份、天、区域来过滤呢?
□ 思路
List<T>.FindAll(Predicate<T> match)方法
Predicate<T>是一个泛型委托,返回bool类型:
public delegate bool Predicate<T>(T obj)
也就是说,match参数是一个返回bool类型的方法,由此,我们可以定义一个泛型类,并在其中自定义返回bool类型的方法,即筛选规则,最后把该方法交给委托。
最终视图界面输入年份、月份、天与订单类中的下单时间属性进行比较。首先想到把年份、月份、天封装到一个类中与订单类进行比较。考虑到有可能存在其它类也有时间属性,都有可能进行有关时间的筛选。这样,可以把时间属性抽象成接口,凡是实现该接口的,都可以与封装年份、月份、天的类进行比较。
class="alt">using System;
namespace ObjectFilter.Interface
{
public interface IDate
{
DateTime Date { get; set; }
}
}
让Order类实现IDate接口。
using System;
using ObjectFilter.Interface;
namespace ObjectFilter.Models
{
public class Order : IDate
{
public int ID { get; set; }
public DateTime Date { get; set; } //接口属性
public string Area { get; set; }
}
}
封装年份、月份、天的类需要面向IDate接口,并提供一个返回bool类型的比较方法。
public class DateFilter<T> where T:IDate
{
private int year;
private int month;
private int day;
public DateFilter(int year, int month, int day)
{
this.year = year;
this.month = month;
this.day = day;
}
public DateFilter(DateTime date) : this(date.Year, date.Month, date.Day){}
public DateFilter(int year, int month) : this(year, month, 0){}
public DateFilter(int year) : this(year, 0, 0){}
public DateFilter() : this(0, 0, 0){}
//自定义规则:只要有T的Date属性值与本类的字段有一个不等,就返回false
public virtual bool MatchRule(T item)
{
if(year != 0 && year !=item.Date.Year)
return false;
if(month != 0 && month != item.Date.Month)
return false;
if(day !=0 && day != item.Date.Day)
return false;
return true;
}
}
如何针对Order中的Area属性筛选呢?可以设计一个DateFilter<Order>的继承类,使子类不但有父类的筛选逻辑,并在此基础上增加一个对Area属性的筛选。
1: public class OrderFilter : DateFilter<Order>
2: {
3: private string area;
4:
5: public OrderFilter(int year, int month, int day, string area)
6: : base(year, month, day)
7: {
8: this.area = area;
9: }
10:
11: public override bool MatchRule(Order item)
12: {
13: bool result = base.MatchRule(item);
14: if (result == false)
15: return false;
16: if (string.IsNullOrEmpty(area) || String.Compare(item.Area, area, true) == 0)
17: {
18: return true;
19: }
20: else
21: {
22: return false;
23: }
24: }
25: }
26:
模拟一个服务层,一个方法用来获取所有订单信息,另一个方法根据条件筛选出订单集合,只需要把自定义泛型类DateFilter<Order>的MatchRule交给Predicate<T>泛型委托就行。
using System;
using System.Collections.Generic;
using ObjectFilter.Helper;
using ObjectFilter.Models;
namespace ObjectFilter
{
public static class Service
{
public static List<Order> GetOrders()
{
return new List<Order>()
{
new Order(){ID = 1,Area = "青岛",Date = new DateTime(2013,8,1)},
new Order(){ID = 2,Area = "平度",Date = new DateTime(2013,9,1)},
new Order(){ID = 3,Area = "即墨",Date = new DateTime(2013,9,3)},
new Order(){ID = 4,Area = "香港",Date = new DateTime(2013,9,3)},
new Order(){ID = 5,Area = "北京",Date = new DateTime(2014,1,2)},
new Order(){ID = 6,Area = "上海",Date = new DateTime(2014,1,2)},
};
}
public static List<Order> GetOrdersBy(DateFilter<Order> filter)
{
return GetOrders().FindAll(new Predicate<Order>(filter.MatchRule));
}
}
}
控制器有2个方法,一个方法返回所有的订单,另一个方法接收来自前台的筛选参数,根据这些参数生成自定义泛型类DateFilter<Order>实例作为服务层方法参数,最终返回部分视图。
using ObjectFilter.Helper;
using ObjectFilter.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace ObjectFilter.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
List<SelectListItem> years = new List<SelectListItem>();
years.Add(new SelectListItem(){Text = "2013",Value = "2013"});
years.Add(new SelectListItem() { Text = "2014", Value = "2014" });
ViewData["y"] = years;
List<SelectListItem> months = new List<SelectListItem>();
for (int i = 1; i <= 12; i++)
{
months.Add(new SelectListItem(){Text =i.ToString(),Value = i.ToString()});
}
ViewData["m"] = months;
List<SelectListItem> days = new List<SelectListItem>();
for (int i = 1; i <= 31; i++)
{
days.Add(new SelectListItem(){Text = i.ToString(),Value = i.ToString()});
}
ViewData["d"] = days;
return View(Service.GetOrders());
}
[HttpPost]
public ActionResult GetOrdersBy(string y, string m, string d, string area)
{
DateFilter<Order> filter = new OrderFilter(Convert.ToInt32(y), Convert.ToInt32(m), Convert.ToInt32(d), area);
return View(Service.GetOrdersBy(filter));
}
}
}
呈现所有订单的视图界面中,当点击"筛选"按钮,把筛选条件通过ajax传递给控制器方法,返回的部分视图最终呈现到本页的一个div中。
logs_code_Collapse">展开
结果:
输入时间筛选:
输入时间和区域筛选:
□ 总结
先把目标对象中需要筛选的属性抽象成接口属性,然后设计一个能接收筛选条件的泛型类,并提供一个返回bool类型的方法,该方法把接收到的来自客户端的筛选条件与目标对象的属性进行比较。最后,只需要把自定义泛型类返回bool类型的方法交给Predicate<T>这个泛型委托即可:FindAll(new Predicate<Order>(filter.MatchRule))。