思维的乐趣

Enjoy it
posts - 84, comments - 63, trackbacks - 0, articles - 0
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

.NET/C#中对自定义对象集合进行自定义排序的方法

Posted on 2010-06-08 15:35 szh114 阅读(...) 评论(...) 编辑 收藏

一个集合可否排序,要看系统知不知道排序的规则,像内建的系统类型,int ,string,short,decimal这些,系统知道怎么排序,而如果一个集合里面放置的是自定义类型,比如自己定义了一个Car类型,要把它排序,系统是不知道怎么办的。

 

那么,如何告知系统排序的规则呢?有以下几种方法:

 

1:对类实现IComparable接口,示例如下:

 

 

代码1
1 using System;
2  using System.Collections.Generic;
3  using System.Linq;
4  using System.Text;
5
6  namespace SortTeset
7 {
8 class Product :IComparable
9 {
10 public string Name { get; private set; }
11 public decimal Price { get; private set; }
12
13 public Product(string name, decimal price)
14 {
15 Name = name;
16 Price = price;
17 }
18
19 public Product() { }
20
21 public static List<Product> GetSampleProduct()
22 {
23 return new List<Product>
24 {
25 new Product{Name="Watch",Price=12345.56m},
26 new Product{Name="Knife",Price=224.50m},
27 new Product{Name="Rope",Price=12.50m},
28 new Product{Name="ETorch",Price=58.5m}
29 };
30 }
31
32 public override string ToString()
33 {
34 return string.Format("{0} : {1}", Name, Price);
35 }
36
37 int IComparable.CompareTo(object obj)
38 {
39 Product temp = (Product)obj;
40 return this.Name.CompareTo(temp.Name);
41 }
42
43 }
44
45 class Program
46 {
47 static void Main(string[] args)
48 {
49 List<Product> ProductSample = Product.GetSampleProduct();
50 foreach (Product tmp in ProductSample)
51 {
52 Console.WriteLine(tmp);
53 }
54
55 Console.WriteLine();
56
57 ProductSample.Sort();
58
59 foreach (Product tmp in ProductSample)
60 {
61 Console.WriteLine(tmp);
62 }
63 }
64 }
65 }

 

 

其中最主要的是这句:

   class Product :IComparable

跟这句:

    int IComparable.CompareTo(object obj)
        {
            Product temp = (Product)obj;
            return this.Name.CompareTo(temp.Name);
        }

 

就是实现了IComparable.CompareTo()这个方法。然后就可以直接调用 SomeProductList.Sort();方法来进行排序。

 

2:指定IComparer  类的对象。

 

 

代码3
1 using System;
2  using System.Collections.Generic;
3  using System.Linq;
4  using System.Text;
5
6  namespace SortTeset2
7 {
8 class Product
9 {
10 public string Name { get; private set; }
11 public decimal Price { get; private set; }
12
13 public Product(string name, decimal price)
14 {
15 Name = name;
16 Price = price;
17 }
18
19 public Product() { }
20
21 public static List<Product> GetSampleProduct()
22 {
23 return new List<Product>
24 {
25 new Product{Name="Watch",Price=12345.56m},
26 new Product{Name="Knife",Price=224.50m},
27 new Product{Name="Rope",Price=12.50m},
28 new Product{Name="ETorch",Price=58.5m}
29 };
30 }
31
32 public override string ToString()
33 {
34 return string.Format("{0} : {1}", Name, Price);
35 }
36 }
37
38
39 class ProductNameComparer : IComparer<Product>
40 {
41 public int Compare(Product first, Product second)
42 {
43 return first.Name.CompareTo(second.Name);
44 }
45 }
46
47 class Program
48 {
49 static void Main(string[] args)
50 {
51 List<Product> ProductSample = Product.GetSampleProduct();
52 foreach (Product tmp in ProductSample)
53 {
54 Console.WriteLine(tmp);
55 }
56
57 Console.WriteLine();
58
59 ProductSample.Sort(new ProductNameComparer());
60
61 foreach (Product tmp in ProductSample)
62 {
63 Console.WriteLine(tmp);
64 }
65 }
66 }
67 }
68  

 

 

这儿我们新定义了一个类:ProductNameComparer,这个类实现了泛型接口:IComparer<Product>,然后在

ProductSample.Sort(new ProductNameComparer());

语句中我们提供了一个比较器对象。

 

这种方法看上去不如直接实现ICompareable接口来得简洁。

 

这种方法可以用匿名方法进行改进:

 

 

代码4
1 using System;
2  using System.Collections.Generic;
3  using System.Linq;
4  using System.Text;
5
6  namespace SortTeset3
7 {
8 class Product
9 {
10 public string Name { get; private set; }
11 public decimal Price { get; private set; }
12
13 public Product(string name, decimal price)
14 {
15 Name = name;
16 Price = price;
17 }
18
19 public Product() { }
20
21 public static List<Product> GetSampleProduct()
22 {
23 return new List<Product>
24 {
25 new Product{Name="Watch",Price=12345.56m},
26 new Product{Name="Knife",Price=224.50m},
27 new Product{Name="Rope",Price=12.50m},
28 new Product{Name="ETorch",Price=58.5m}
29 };
30 }
31
32 public override string ToString()
33 {
34 return string.Format("{0} : {1}", Name, Price);
35 }
36 }
37
38 class Program
39 {
40 static void Main(string[] args)
41 {
42 List<Product> ProductSample = Product.GetSampleProduct();
43 foreach (Product tmp in ProductSample)
44 {
45 Console.WriteLine(tmp);
46 }
47
48 Console.WriteLine();
49
50 ProductSample.Sort(delegate(Product first, Product second)
51 {
52 return first.Name.CompareTo(second.Name);
53 });
54
55 foreach (Product tmp in ProductSample)
56 {
57 Console.WriteLine(tmp);
58 }
59 }
60 }
61 }
62  

 

 

这一次,不用定义那个类,然后使用它的方法了,而是直接填充delegate接口。

 

这种方法还可以进一步用Lambda表达式改进,如下:

 

 

代码5
1 using System;
2  using System.Collections.Generic;
3  using System.Linq;
4  using System.Text;
5
6  namespace SortTeset4
7 {
8 class Product
9 {
10 public string Name { get; private set; }
11 public decimal Price { get; private set; }
12
13 public Product(string name, decimal price)
14 {
15 Name = name;
16 Price = price;
17 }
18
19 public Product() { }
20
21 public static List<Product> GetSampleProduct()
22 {
23 return new List<Product>
24 {
25 new Product{Name="Watch",Price=12345.56m},
26 new Product{Name="Knife",Price=224.50m},
27 new Product{Name="Rope",Price=12.50m},
28 new Product{Name="ETorch",Price=58.5m}
29 };
30 }
31
32 public override string ToString()
33 {
34 return string.Format("{0} : {1}", Name, Price);
35 }
36 }
37
38 class Program
39 {
40 static void Main(string[] args)
41 {
42 List<Product> ProductSample = Product.GetSampleProduct();
43 foreach (Product tmp in ProductSample)
44 {
45 Console.WriteLine(tmp);
46 }
47
48 Console.WriteLine();
49
50 ProductSample.Sort((first,second)=>first.Name.CompareTo(second.Name));
51
52 foreach (Product tmp in ProductSample)
53 {
54 Console.WriteLine(tmp);
55 }
56 }
57 }
58 }
59  

 

 

变态的是,还可以进一步改进,使用扩展方法,如下:

 

 

代码6
1 using System;
2  using System.Collections.Generic;
3  using System.Linq;
4  using System.Text;
5
6  namespace SortTeset5
7 {
8 class Product
9 {
10 public string Name { get; private set; }
11 public decimal Price { get; private set; }
12
13 public Product(string name, decimal price)
14 {
15 Name = name;
16 Price = price;
17 }
18
19 public Product() { }
20
21 public static List<Product> GetSampleProduct()
22 {
23 return new List<Product>
24 {
25 new Product{Name="Watch",Price=12345.56m},
26 new Product{Name="Knife",Price=224.50m},
27 new Product{Name="Rope",Price=12.50m},
28 new Product{Name="ETorch",Price=58.5m}
29 };
30 }
31
32 public override string ToString()
33 {
34 return string.Format("{0} : {1}", Name, Price);
35 }
36 }
37
38 class Program
39 {
40 static void Main(string[] args)
41 {
42 List<Product> ProductSample = Product.GetSampleProduct();
43 foreach (Product tmp in ProductSample)
44 {
45 Console.WriteLine(tmp);
46 }
47
48 Console.WriteLine();
49
50 foreach (Product tmp in ProductSample.OrderBy(p=>p.Name))
51 {
52 Console.WriteLine(tmp);
53 }
54 }
55 }
56 }

 

 

“这里似乎调用了一个OrderBy方法,但查阅一下MSDN,就会发现这个方法在List<Product>中根本不存在。之所以能调用它,是由于存在一个扩展方法。这里实际不再是"原地"对列表进行排序,而只是按特定的顺序获取列表的内容。有的时候,你需要更改实际的列表;但有的时候,没有任何副作用的排序显得更"善解人意"。重点在于,现在的写法更简洁,可读性更好(当然是在你理解了语法之后)。我们的想法是"列表按名称排序",现在的代码正是这样做的。并不是"列表通过将一个产品的名称与另一个产品的名称进行比较来排序",就像C# 2代码所做的那样。也不是使用知道如何将一个产品与另一个产品进行比较的另一个类型的实例来按名称排序。这种简化的表达方式是C# 3的核心优势之一。既然单独的数据查询和操作是如此的简单,那么在执行更大规模的数据处理时,仍然可以保持代码的简洁性和可读性,这进而鼓励开发者以一种"以数据为中心"的方式来观察世界。”

 

最后这两步的语法,绝对是一个会用其它语言比如C/C++,VB的人所无法明白的,C#进化速度真是快。。。。。。。

 

关于排序,暂时还不知道有没有其它的方法,日后再补记。

 

参考书籍:《C#与.NET3.5高级程序设计》,《深入解析C#》(C# in depth) 。

 

另外,这篇文章还提到了另外两种更深入的做法:1,自定义排序,可以提供一个接口,接受一个排序指标,然后对指定List进行排序,2,用LINQ的orderby子句实现排序。

 

以自定义方式可以实现的更多的控制,例如我们重构BookComparison:

// Release : code01, 2009/04/12

 // Author  : Anytao, http://www.anytao.com/

 // List    : BookComparison.cs

public class BookComparison : IComparer<Book>{

    private ComparisonType type;

    public BookComparison(ComparisonType type)

    {        this.type = type;    }

    public int Compare(Book x, Book y)

    {

        switch (this.type) 

       {

            case ComparisonType.Price:

                return x.Price.CompareTo(y.Price);

                break;

            case ComparisonType.PublishDate:

                return x.PublishDate.CompareTo(y.PublishDate);

                break;

            default:

                break;

        }

        return 0;

    }

}

添加一个ComparisonType结构,在BookComparson初始化时决定Comparison的方式:

//04 Sort by custom comparison: BookComparison

bs.Books.Sort(new BookComparison(ComparisonType.PublishDate).Compare);

结论

自定义Comparison为实现更好的Sort控制,提供了很好的扩展机制。在我们的实际应用中,对于例如BookStore这样的具体应用而言,我更推荐以LINQ的OrderBy来实现,例如:

//05 Sort by Linq

var list = from c in bs.Books

           orderby c.PublishDate ascending

           select c;

foreach (var item in list)

{

    Console.WriteLine(string.Format("{0}:{1}, {2}", item.Name, item.Price, item.PublishDate.ToString()));

}

orderby子句可以选择任意的排序条件,同时ascending或者descending控制升序和降序。