Loading [MathJax]/jax/input/TeX/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >基于业务对象(列表)的排序

基于业务对象(列表)的排序

作者头像
张子阳
发布于 2018-09-30 02:20:24
发布于 2018-09-30 02:20:24
2.3K0
举报

基于业务对象(列表)的排序

2008-3-21 作者: 张子阳 分类: 设计与模式

引言

在上一篇文章 基于业务对象的筛选 中,我们讨论了如何实现Predicate<T>(T object)委托,自定义DateFilter 类来对业务对象进行筛选。与筛选一样,排序也是常见且重要的操作。在对业务对象进行排序时,不能使用ObjectDataSource作为数据源,因为它只对 DataView、DataTable 和 DataSet 支持自动排序。但你仍可以对GridView编写Sorting事件的处理方法,通过拼装SQL语句,使用“Order By”子句来完成排序。

和进行筛选的思路一样,如果我们将业务对象缓存在服务器上,第一次访问时从数据库提取数据,然后进行缓存,后继的请求只针对缓存了的业务对象进行,则可以降低对数据库的依赖,提高效率。本文将讨论如何对获取的业务对象进行排序,包括简单排序、任意列排序、以及多列复合排序。

本文是接着上一篇写的,一些重复的内容本文将不再讲述,建议先阅读 基于业务对象的筛选

简单排序 - 对固定属性的默认排序

与上篇文章不同,我不再说明使用拼装SQL来完成排序的方式,我们直接看基于List<Order>对象的排序。我们知道List<T>提供了Sort()方法来进行排序操作,那么它又如何使用呢?我们先创建一个ObjSort.aspx文件,然后在代码后置中添加如下代码:

代码语言:txt
AI代码解释
复制
protected void Page_Load(object sender, EventArgs e)
 {
     Label lb1 = new Label(); 

     List<int> list = new List<int>();
     list.Add(4);
     list.Add(5);
     list.Add(2);
     list.Add(9);
     list.Add(1);

     foreach (int item in list) {
        lb1.Text += item.ToString() + ", ";
     }

     form1.Controls.Add(lb1);
     HtmlGenericControl hr = new HtmlGenericControl("hr");
     form1.Controls.Add(hr);

     Label lb2 = new Label();
     list.Sort();     // 对列表进行排序
     foreach (int item in list) {
        lb2.Text += item.ToString() + ", ";
     }
     form1.Controls.Add(lb2);
 }

可以看到,通过在List<int>上使用Sort()方法,对列表中的元素进行了排序。现在我们在OrderManager.cs中新添一个方法GetSortList(),它用于获取列表对象,因为GetList()方法返回的记录数太多,而在本文中我们仅关注排序,所以我们仅返回15条记录。

代码语言:txt
AI代码解释
复制
// 获取用于排序的列表
public static List<Order> GetSortList() {

    List<Order> list = HttpContext.Current.Cache["sortList"] as List<Order>;

    if (list == null) {
       list = GetList("Select Top 15 OrderId, CustomerId, ShipCountry, OrderDate From Orders");
       HttpContext.Current.Cache.Insert("sortList", list);
    }

    return list;
}

如果你没有看上一篇文章,那么只要知道这个方法返回一个List<Order>类型的业务对象,代表一个订单列表就可以了(Order对象包含四个公共属性,分别是OrderId, CustomerId, OrderDate, Country)。然后我们创建 ObjSort2.aspx文件,在它上面拖放一个Reperter控件,并编写一些代码,用于显示一个表格:

代码语言:txt
AI代码解释
复制
<asp:Repeater runat="server" ID="rpOrderList" >
    <HeaderTemplate>
     <table>
        <tr>
            <th>
               <asp:LinkButton ID="lbtOrderId" runat="server">OrderId</asp:LinkButton>
            </th>
            <th>
               <asp:LinkButton ID="lbtCustomerId" runat="server">CustomerId</asp:LinkButton>
            </th>
            <th>
               <asp:LinkButton ID="lbtOrderDate" runat="server">OrderDate</asp:LinkButton>
            </th>
             <th>
                <asp:LinkButton ID="lbtCountry" runat="server">Country</asp:LinkButton>
            </th>
        </tr>
    </HeaderTemplate>
    <ItemTemplate>
        <tr>
            <td><%#Eval("OrderId") %></td>
            <td><%#Eval("CustomerId") %></td>
            <td><%#Eval("OrderDate") %></td>
             <td><%#Eval("Country") %></td>
        </tr>
    </ItemTemplate>       
    <FooterTemplate>      
     </table>
    </FooterTemplate>     
 </asp:Repeater>

然后,我们在后置代码ObjSort2.aspx.cs的Page_Load事件中,添加这样两行语句:

代码语言:txt
AI代码解释
复制
rpOrderList.DataSource = OrderManager.GetSortList();
rpOrderList.DataBind();

然后再打开页面,可以看到在页面上输出了列表。现在我们想对这个列表进行排序,那么我们仿照List<int>的做法,修改上面的代码:

代码语言:txt
AI代码解释
复制
List<Order> list = OrderManager.GetSortList();
list.Sort();    // 期望可以进行排序
rpOrderList.DataSource = list;
rpOrderList.DataBind();

实际上,我们会得到错误:必须至少有一个对象实现 IComparable。

IComparable<T>接口

我们就是自己想也应该想到为什么会出错:Order对象包含了四个属性OrderId、CustomerId、OrderDate、Country,而int只有它本身的值,所以,当我们在List<Order>上调用Sort()的时候,列表对象根本不知道应该如何排序,也不知道以哪个属性来进行排序。而IComparable接口,定义了如何进行排序的规则,如果我们想要对List<Order>对象进行排序,那么我们就需要让列表的元素,也就是Order对象实现这个接口。实际上,List<int>之所以可以直接调用Sort()方法,是因为int,以及几乎全部的基本类型(比如string,char,datetime等),本身就实现了IComparable<T>。

代码语言:txt
AI代码解释
复制
public interface IComparable<T> {
    int CompareTo(T other);
}

这个接口只需要实现一个方法,CompareTo(),它传递与要比较的对象(列表中的当前对象)同类型的另一个对象 other,返回一个int类型的值:小于零 当前对象小于 other 参数。零 此对象等于 other。大于零 当前对象大于 other。

现在我们让Order对象(Order参见下载的代码)实现这个接口:

代码语言:txt
AI代码解释
复制
// 实现 IComparable<T> 接口 
public int CompareTo(Order other) { 
    return this.CustomerId.CompareTo(other.CustomerId); 
}

我们将排序的规则委托给了CustomerId去处理,因为CustomerId是一个string类型,调用了它的CompareTo()方法。这样,在List<Order>上调用Sort()的时候就会依据这里定义的规则,以CustomerId进行排序了。再次打开ObjSort.aspx,应该可以看到列表按CustomerId进行了排序。

高级排序 - 多个属性组合排序

IComparer<T> 接口

上面仅仅是为列表提供了一个默认排序,实际上,我们经常要求对多个列进行排序,我们还会要求按降序或者升序进行排序,我们甚至会要求对多个列的组合进行排序,比如:先对CustomerId进行升序排列,再对OrderDate降序排列。此时虽然使用CompareTo(Order other)也可以实现,但是要给Order对象添加额外的字段或者属性,这些.Net Framewok已经考虑到了,并提供了IComparer<T>接口封装了排序规则,我们可以通过实现这个接口来完成排序。

代码语言:txt
AI代码解释
复制
public interface IComparer<T> { 
    int Compare(T x, T y);
}

IComparer<T>只需要实现一个方法,Compare()它接受两个同一类型的参数,并返回int类型的结果,与IComparable<T>类似,当返回值小于0时,x小于y;等于0时,x等于y;大于0时,x大于y。需要注意的是:这个接口不是要求我们让Order对象实现它,而是要求另外一个对象实现它,比如OrderComparer,而在调用Sort()方法时,将它作为参数传递进去。因为这个OrderComparer只是用于对Order对象进行排序,不能应用于其他对象,所以我们将它声明为Order的嵌套类。

实现 IComparer<T>接口

打开Order.cs文件,对它进行如下修改,先添加一个枚举SortDirection,用于表示排序的方向:

代码语言:txt
AI代码解释
复制
// 可复用的枚举,表示排序的方向 
public enum SortDirection { 
    Ascending = 0, 
    Descending 
}

在Order类的内部,添加一个枚举,这个枚举类型代表了可以进行排序的属性:

代码语言:txt
AI代码解释
复制
// 嵌套枚举,仅应用于此业务对象,可排序的属性
public enum SortField {
    OrderId,
    CustomerId,
    OrderDate,
    Country
}

我们还需要再定义一个结构Sorter,这个结构包含两个字段,一个SortDirection类型,一个SortField类型,它封装了排序的必要信息:对于哪个属性按照哪种方式(升序或降序)排序。由于这个结构依然是只针对Order对象的,所以我们还是把它定义在Order内部:

代码语言:txt
AI代码解释
复制
// 嵌套结构,仅应用于此业务对象,排序的属性和方式
public struct Sorter {
    public SortField field;
    public SortDirection direction;

    public Sorter(SortField field, SortDirection direction) {
       this.field = field;
       this.direction = direction;
    }

    public Sorter(SortField field) {
       this.field = field;
       this.direction = SortDirection.Ascending;
    }
}

接着,我们在Order内部定义实现IComparer<T>的类OrderComparer:

代码语言:txt
AI代码解释
复制
// 嵌套类,仅对于此业务对象进行排序 
public class OrderComparer : IComparer<Order> { 

}

现在考虑如何实现它:因为我们要实现对某个属性,按某种方式排序,那么我们至少要将这两个参数传进去,所以OrderCompare应该包含字段用于维护SortDirection和SortField;因为我们期望可以对多个属性组合排序,所以应该维护一个它们的列表,而SortDirection和SortFiled,已经包含在了Sorter结构中,所以它只要维护一个List<Sorter>结构就可以了:

代码语言:txt
AI代码解释
复制
public class OrderComparer : IComparer<Order> {
    private List<Sorter> list;
    // 构造函数,设定排序字段列表
    public OrderComparer(List<Sorter> list) {
        this.list = list;
    }
}

接着考虑如何排序,先从简单入手,我们不考虑对于多个属性的排序,只对某个属性按某种方式排序,那么我们需要添加一个方法CompareTo(),它接受排序的属性、排序的方式,以及排序的两个对象,最后返回int类型,说明这两个对象的大小(位置的先后):

代码语言:txt
AI代码解释
复制
// 对单个属性按某种方式进行排序
public int Compare(Order x, Order y, SortField field, SortDirection direction) {
    int result = 0;          // 默认排序位置不变化

    switch (field) {
       case SortField.Country:
           if (direction == SortDirection.Ascending)
              result = x.Country.CompareTo(y.Country);
           else
              result = y.Country.CompareTo(x.Country);
           break;
       case SortField.CustomerId:
           if (direction == SortDirection.Ascending)
              result = x.CustomerId.CompareTo(y.CustomerId);
           else
              result = y.CustomerId.CompareTo(x.CustomerId);
           break;
       case SortField.OrderDate:
           if (direction == SortDirection.Ascending)
              result = x.OrderDate.CompareTo(y.OrderDate);
           else
              result = y.OrderDate.CompareTo(x.OrderDate);
           break;
       case SortField.OrderId:
           if (direction == SortDirection.Ascending)
              result = x.OrderId.CompareTo(y.OrderId);
           else
              result = y.OrderId.CompareTo(x.OrderId);
           break;
    }

    return result;
}

但是这个方法不会实现IComparer<T>接口,也没有办法进行多个列的排序。继续进行之前,我们考虑下如何对两个对象的多个属性(比如A、B、C)来进行排序:先对属性A进行比较,如果属性A相同,继续比较属性B,如果属性B相同,继续比较属性C。在这个过程中,只要有任意一个属性不相同,就可以决定两个对象的先后顺序,也就是不再进行后面属性的比较。

有了思路,我们现在实现IComparer<T>接口,编写方法

代码语言:txt
AI代码解释
复制
// 实现 IComparer接口
public int Compare(Order x, Order y) {
    int result = 0;
    foreach (Sorter item in list) {
       result = Compare(x, y, item.field, item.direction);
       if (result != 0)      // 一旦result不为0,则已经区分出位置大小,跳出循环
           break;
    }

    return result;
}

在这个方法中,我们遍历了List<Sorter>,并且在foreach语句中调用了我们前面定义的对单个属性的比较方法Compare(Order x, Order y, SortField field, SortDirection direction),一旦比较结果不为0,那么就跳出循环。

好了OrderComparer类的实现已经完成了,我们再看下还有什么可以完善的地方:如果以后每次调用Sort进行排序的时候,都要先需要先创建列表,指定排序规则,构造OrderCompare对象,显然会很麻烦,所以我们给在Book类中添加一组重载了的方法GetComparer(),用来简化以后调用时的操作步骤:

代码语言:txt
AI代码解释
复制
// 指定排序属性 和 排序方式
public static OrderComparer GetComparer(SortField field, SortDirection direction) {
    List<Sorter> list = new List<Sorter>();
    Sorter sorter = new Sorter(field, direction);
    list.Add(sorter);
    return new OrderComparer(list);
}

// 指定排序属性,默认为升序
public static OrderComparer GetComparer(SortField field) {
    return new OrderComparer(field, SortDirection.Ascending);
}

// 默认为以OrderId升序排列
public static OrderComparer GetComparer() {
    return new OrderComparer(SortField.OrderId, SortDirection.Ascending);
}

// 排序列表
public static OrderComparer GetComparer(List<Sorter> list) {
    return new OrderComparer(list);
}

好了,现在OrderComparer类就全部创建好了,我们接下来看一下如何使用它:

页面调用

我们修改一下代码后置文件,来看下如何进行设置,我们将Sort()改成这样:

代码语言:txt
AI代码解释
复制
list.Sort(Order.GetComparer(Order.SortField.Country, SortDirection.Descending)); // 以Country倒序排列

然后查看页面,发现列表以Country属性进行了倒序排列。那如果我们想先按Country倒序排列,再按CustomerId顺序排列,又该如何呢?

代码语言:txt
AI代码解释
复制
// 以Country降序, CustomerId升序排列
List<Order.Sorter> sorterList = new List<Order.Sorter>(); //先创建sorterList
sorterList.Add(new Order.Sorter(Order.SortField.Country, SortDirection.Descending));     // 先以Country Desc排序
sorterList.Add(new Order.Sorter(Order.SortField.CustomerId));//再以CustomerId Asc
list.Sort(Order.GetComparer(sorterList));

现在打开页面,可以看到列表以我们期望的方式进行了排序。在本文中,由于仅仅是出于示范的目的,所以我们在代码中直接书写了用于排序的SortList,实际上这些应该是基于用户选择而动态创建的。在ObjSort2.aspx页面上,表格的标题我使用了LinkButton,有兴趣的话可以编写LinkButton的Click事件,来动态地实现这一排序过程。

总结

本文详细的讨论了如何对列表(业务对象)进行排序。

我们首先了解IComparable<T>接口,学习了如何实现这个接口以实现针对某一字段的一个默认排序。接着,我们详细地讨论了如何通过实现一个IComparer<T>接口,来实现可以对任意单个属性以及多个属性组合的排序。

大家可以看到,一旦掌握了方法以后,再编写诸如OrderComparer这样的代码是枯燥无味的,以后我们再一起看看如果利用反射来编写一个小程序为我们自动地生成这些代码。

感谢阅读,希望这篇文章能给你带来帮助!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2008-3-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
在ListView中实现排序
ListView触发数据源排序,使用数据源(即List<T>)的Sort()方法,又一次绑定数据源到ListView。
全栈程序员站长
2022/09/06
1.6K0
基于业务对象(列表)的筛选
可能大家对SQL语句太过熟悉了,也可能虽然已经从Asp过度到了Asp.Net时代,但是Asp的观念没有发生太大变化。结果就是我们将应用程序大部分的逻辑都交给了数据库去完成,忘记了.Net Framework提供给我们灵活强大的数据操控能力。比如说,当我们需要对数据进行筛选的时候,我们想到的是“Where”子句,而不是List<T>.FindAll();当我们需要对数据进行排序的时候,我们想到的是“Order By”子句,而不是List<T>.Sort();当我们需要对数据进行分页的时候我们想到的是存储过程,而不是List<T>.GetRange()。
张子阳
2018/09/30
2.2K0
基于业务对象(列表)的筛选
DataGridView绑定BindingList<T>带数据排序的类
本文章转载:http://yuyingying1986.blog.hexun.com/30905610_d.html
跟着阿笨一起玩NET
2018/09/19
1.9K0
GridView实战一:自定义分页、排序、修改、插入、删除
前言:   在某次公司面试时被问到对GridView操作的熟悉程度,在那之前一直用Repeater内嵌table标签对GridView操作确实很少,于是最近在项目的后台上对GridView进行了一番实操,本文和后面的另一篇GridView实战二:使用ObjectDataSource数据源控件均是这段时间的一些总结。   GridView优点就是集数据绑定、分页、排序、删、改于一身,提高了开发效率;缺点嘛,就是运行效率低,并且它本身不带添加功能。于是GridView用于后台开发是一个不错的选择,而前台页面还是
^_^肥仔John
2018/01/18
3K0
GridView实战一:自定义分页、排序、修改、插入、删除
C# 中的IComparable和IComparer
在开发过程中经常会遇到比较排序的问题,比如说对集合数组的排序等情况,基本类型都提供了默认的比较算法,如string提供了按字母进行排序,而int整数则是根据整数大小进行排序.但是在引用类型中(具有多个字段),那么这个排序当然也是取决于我们特定的值。
HueiFeng
2020/01/22
6340
C# 中的IComparable和IComparer
C#List的排序和简单去重总结
      List集合在开发过程中很常见,经常我们要对该集合进行一系列操作,本文介绍如何将该集合内的元素进行排序,博主制作简单WinForm应用程序进行演示。       首先,我们来看一下c#泛型List提供的Sort方法: 这里有泛型List类的Sort方法的三种形式,分别是 1,不带有任何参数的Sort方法----Sort(); 这种排序List中的元素必须继承IComparable接口,并且要实现IComparable接口中的CompareTo()方法,在CompareTo()方法中要实现比较规则
用户1055830
2018/01/18
1.3K0
C#List的排序和简单去重总结
C# 泛型集合的自定义类型排序
int类型是实现了IComparable这个接口的。那么如果让自定义类型Person也可以排序,那么试试实现该接口;
用户7053485
2020/11/24
1.5K0
C# 泛型集合的自定义类型排序
编写高质量代码改善C#程序的157个建议[10-12]
  本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html 。本文主要学习记录以下内容:
aehyok
2018/08/31
4250
编写高质量代码改善C#程序的157个建议[10-12]
【100个 Unity实用技能】| C# 中 Sort() 对List中的数据排序的几种方法 整理总结
在C#中我们会经常用到List<>作为一个容器使用,在使用的过程中往往要对集合中的数据进行排序操作。
呆呆敲代码的小Y
2022/11/12
3K0
【100个 Unity实用技能】| C# 中 Sort() 对List中的数据排序的几种方法 整理总结
快速排序与三路快速排序
快速排序是非常常用的排序方法, 采用分治法的策略将数组分成两个子数组, 基本 思路是:
beginor
2020/08/10
5840
快速排序与三路快速排序
.NET面试基础知识
不同公司的职位和工作职责不同。在面试中,工作职责和经验对这个职位很重要。程序员职位有一年的经验他们会关注oops概念、并行编程、算法和解决问题的能力等等。另一方面,如果这个职位需要夫妻几年经验之后,他们可以专注于最新的框架、语言及其特点,单元测试概念、场景基础问题,软件开发方法,设计原则,设计模式,最佳实践指导方针和软件架构等。
程序你好
2018/09/29
9260
.NET面试基础知识
浅谈C#数组(一)
  如果需要使用同一类型的多个对象,可以使用数组和集合(后面介绍)。C#用特殊的记号声明,初始化和使用数组。Array类在后台发挥作用,它为数组中的元素排序和过滤提供了多个方法。使用枚举器,可以迭代数组中的所有元素。   如果需要使用不同类型的多个对象,可以使用Tuple(元组)类型。
全栈程序员站长
2022/09/07
1K0
浅谈C#数组(一)
DotNet常用排序算法总结
    数据结构和算法对一个程序来说是至关重要的,现在介绍一下几种算法,在项目中较为常用的算法有:冒泡排序,简单选择排序,直接插入排序,希尔排序,堆排序,归并排序,快速排序等7中算法。  
彭泽0902
2018/01/04
7160
DotNet常用排序算法总结
WPF中ListView排序实现「建议收藏」
下面的代码支持ListView 多个列点击排序,并会记住点击过的每个列的排序方向 。
全栈程序员站长
2022/11/14
1K0
浅谈泛型
我们在写一些通用库的时候,经常需要写一个算法,比如交换,搜索,比较,排序,转换等算法,但是需要支持int,string等多种类型。通常我们可能会把代码复制多遍分别处理不同类型的数据。有没有一种办法,让我们只写一遍算法的实现,就可以支持所有类型的数据?泛型(generic)是C#提供的一种机制,它可以提供这种形式的代码重用,即“算法重用”。简单来说,开发人员在定义算法的时候并不设定算法操作的数据类型,而是在使用这个算法的时候再指定具体的数据类型。大多数算法都封装在一个类型中,CLR允许创建泛型引用类型和泛型值类型,以及泛型接口和泛型委托。所以CLR允许在类或接口中定义泛型方法。来看一个简单例子,Framework类库定义了一个泛型列表算法,它知道如何管理对象集合。泛型算法没有设定数据的类型。要在使用这个泛型列表算法时指定具体的数据类型。封装了泛型列表算法的FCL类称为List<T>。这个类是System.Collections.Generic命名空间中定义的。下面展示了类的定义:
小蜜蜂
2019/07/14
1.2K0
利用 IComparable<T> 以及 IComparer<T> 定义顺序关系
我们都知道,在开发中如果想把某个类型的对象放入集合中执行排序和搜索功能,就需要定义出来对象与对象之间的关系。那么你知道怎么样定义对象关系才是正确的吗?下面就听我一一道来。
喵叔
2020/11/11
6150
C# Sort排序
2.对于非数值类型或者自定义类型,可通过实现IComparable接口重写CompareTo方法来排序:
全栈程序员站长
2022/09/14
5980
c# listview排序功能_c#入门
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections; using System.Windows.Forms;
全栈程序员站长
2022/11/09
6800
C#中的泛型
.Net 1.1版本最受诟病的一个缺陷就是没有提供对泛型的支持。通过使用泛型,我们可以极大地提高代码的重用度,同时还可以获得强类型的支持,避免了隐式的装箱、拆箱,在一定程度上提升了应用程序的性能。本文将系统地为大家讨论泛型,我们先从理解泛型开始。
张子阳
2018/09/30
1.4K0
asp.net mvc4 使用KindEditor文本编辑器
  最近做项目要用文本编辑器,编辑器好多种,这里介绍KindEditor在asp.net mvc4中的使用方法。   一、准备工作:     1.下载KindEditor.去官网:http://www.kindsoft.net/     2.解压,拷贝到项目中(这里面有些例子,可以先删除掉在拷贝到项目中。)   二、使用步骤:     1.view页面 <script> var editor; KindEditor.ready(function (K) { editor =
阿炬
2018/05/11
1.1K0
相关推荐
在ListView中实现排序
更多 >
LV.1
这个人很懒,什么都没有留下~
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档