轻轻松松学习Linq排序

开发 后端
Linq排序在一系列Linq操作中应该使用频率最高的,关于Linq排序的文章也很多,但是笔者的这篇文章最值得一读了,因为他把理论与实践结合的十分完美,理解起来也很简单,希望能给你带来帮助。

Linq排序在一系列Linq操作中应该使用频率***的,关于Linq排序的文章也很多,但是笔者的这篇文章最值得一读了,因为他把理论与实践结合的十分***,理解起来也很简单,希望能给你带来帮助。

在程序开发中,对数据进行排序是很常见的操作。现在就来演示一下Linq排序,假设现在有一个类Customer,定义如下所示:

  1. public class Customer  
  2. {  
  3. public string Id { getset; }  
  4. public string Name { getset; }  
  5. public decimal Age { getset; }  

我们现在要对很多Customer对象进行排序,最简单的就是使用Linq排序的orderby子句:

  1. from c in Customers orderby c.Id select c; 

上面实现了按照Id来进行Linq排序。可是需求变了,用户现在想用Name来排序。好办!把上面的改一改比如下面这样就可以了:

  1. from c in Customers orderby c.Name select c; 

可是需求又变了,用户现在说,你在程序中不能写死,得列一个菜单,我点哪个你就按哪个给我Linq排序。这个也不难办:

  1. var searchResult = from c in Customers select c;  
  2. if (columnName == "Id")  
  3. {  
  4. searchResult = from c in Customers orderby c.Id select c;  
  5. }  
  6. else if (columnName == "Name")  
  7. {  
  8. searchResult = from c in Customers orderby c.Id select c;  
  9. }  
  10. else ...  

这确实解决了问题,可是这样的代码不易维护。如果加属性了怎么办?如果属性改名字了怎么办?如果有好多的不同的类都需要这样的Linq排序怎么办?

下面我介绍一种较为通用的解决方案,此方案的核心技术是反射(Reflection)和扩展方法(Extension Methods)。

  1. public static IOrderedQueryable OrderBy(  
  2. this IQueryable source,  
  3. Expression> keySelector,  
  4. IComparer comparer  

参数source是一个用来排序的对象,keySelector是用来取出用来Linq排序的键的函数,comparer(比较器)用来比较取出的两个键值。

对象的属性类型可能多种多样,而我们又不想为每种类型分别指定comparer(因为那样做的话也将是一堆if else…)。变通一下思路,不管遇到哪种类型的属性,我们都先把它的值放到一个共同的容器中,然后为这个容器写一个comparer类。我们把类型判断留到了这个comparer中,因为类型是有限的,至少我们需要处理的那些属性的类型是有限的。
上面提到的这个值的容器也是一个类,定义如下:

  1. public class CommonComparableValue  
  2. {  
  3. public object RealValue { getset; }  

相应的比较类定义如下:

  1. public class CommonComparableValueComparer : IComparer  
  2. {  
  3. public int Compare(CommonComparableValue x, CommonComparableValue y)  
  4. {  
  5. string s = x.RealValue as string;  
  6. if (s != null)  
  7. {  
  8. return s.CompareTo(y.RealValue);  
  9. }  
  10. int? i = x.RealValue as int?;  
  11. if (i != null)  
  12. {  
  13. return i.Value - (int)y.RealValue;  
  14. }  
  15. decimal? d = x.RealValue as decimal?;  
  16. if (d != null)  
  17. {  
  18. return d.Value.CompareTo((decimal)y.RealValue);  
  19. }  
  20. throw new NotImplementedException("NotImplemented Data Type!!!");  
  21. }  

这里的比较类只用到了int,string等几种类型,如果属性有其它的类型,也应该在这里添加。从代码实现可以看出,即使属性的类型是复杂数据类型也可以这么处理。

现在来看keySelector的实现。它是用来取出待比较的属性值的函数。这个函数应该是这个样子的:

  1. public delegate TResult Func  
  2. ( T  arg ) 

在这里,T的类型就是Customer,TResult就是刚刚已经那个存放任意属性类型的值的容器CommonComparableValue。

在这个实现取键值的函数里,我们只有一个Customer类型的参数arg可用,而那个用来Linq排序的属性名字是在运行期间确定的,如何才能取出我们想要的属性的值呢?方法是这样的,先通过扩展方法为Customer加一个名为GetSortingKeyValue的取键值方法,代码如下:

  1. public static class CustomerSortExtension  
  2. {  
  3. public static CommonComparableValue GetSortingKeyValue(this Customer ainfo, string columnName)  
  4. {  
  5. Type t = ainfo.GetType();  
  6. PropertyInfo pinfo = t.GetProperty(columnName);  
  7. if (pinfo == null)  
  8. {  
  9. throw new Exception("Property " + columnName + "not found");  
  10. }  
  11. else 
  12. {  
  13. return new CommonComparableValue  
  14. {  
  15. RealValue = pinfo.GetValue(ainfo, null)  
  16. };  
  17. }  
  18. }  

这里就是通过一个字符串获取属性值,核心是反射。接下来只要在那个keySelector方法中调用GetSortingKeyValue方法就可以了。

到这里,各种准备活动就做完了。现在来看一下怎么把这些东西组织起来实现Linq排序:

  1. var searchResult = from c in Customers select c;  
  2. Func myFunc = x => x.GetSortingKeyValue(columnName);  
  3. CommonComparableValueComparer comparer = new CommonComparableValueComparer();  
  4. searchResult = searchResult.OrderBy(myFunc, comparer); 

这种方案的主要内容到这里就介绍完了。

***提一下,如果你想把这些东西用到你的代码中,你一般需要做的只有:将扩展方法的***个参数改为你需要Linq排序的那个类型。比如要为Person排序,扩展方法则可以是这个样子:

  1. public static CommonComparableValue GetSortingKeyValue(this Customer ainfo, string columnName) 

当然,那个CommonComparableValueComparer类也应该根据实际类型修改以支持更多的属性类型。

以上就是对Linq排序的详细介绍,从理论到方法,很有价值的一篇文章呦!

【编辑推荐】

  1. 为你揭晓 Linq更新数据是否真的实用?
  2. 深度剖析linq级联删除
  3. 简单实现Linq连接查询
  4. LINQ动态查询的实现浅析
  5. 简单实现Linq多条件查询
责任编辑:阡陌 来源: 博客园
相关推荐

2010-03-03 18:13:23

Android组成框架

2010-01-14 16:10:21

C++开发

2010-01-15 10:14:21

C++ Builder

2009-11-10 15:44:17

VB.NET常量

2010-01-18 11:20:58

C++语言

2009-11-09 15:41:14

WCF安全性

2010-03-01 17:32:21

Python 测试模块

2010-01-26 14:53:43

C++

2010-01-20 18:17:55

C++异常问题

2010-01-20 10:31:18

C++编程技术

2009-06-10 17:58:41

2017-10-27 12:00:28

MySQL数据库优化

2010-08-30 09:27:20

2015-09-22 10:31:11

2011-04-28 15:41:02

打印机卡纸

2019-11-28 10:21:01

MySQLDocker数据

2009-11-11 09:31:44

ADO.NET事务处理

2012-01-05 10:23:33

IE9预订火车票

2021-11-07 06:52:44

Windows 11操作系统微软

2009-02-20 13:39:01

点赞
收藏

51CTO技术栈公众号