輕輕松松學(xué)習(xí)Linq排序
Linq排序在一系列Linq操作中應(yīng)該使用頻率***的,關(guān)于Linq排序的文章也很多,但是筆者的這篇文章最值得一讀了,因?yàn)樗牙碚撆c實(shí)踐結(jié)合的十分***,理解起來(lái)也很簡(jiǎn)單,希望能給你帶來(lái)幫助。
在程序開(kāi)發(fā)中,對(duì)數(shù)據(jù)進(jìn)行排序是很常見(jiàn)的操作?,F(xiàn)在就來(lái)演示一下Linq排序,假設(shè)現(xiàn)在有一個(gè)類Customer,定義如下所示:
- public class Customer
- {
- public string Id { get; set; }
- public string Name { get; set; }
- public decimal Age { get; set; }
- }
我們現(xiàn)在要對(duì)很多Customer對(duì)象進(jìn)行排序,最簡(jiǎn)單的就是使用Linq排序的orderby子句:
- from c in Customers orderby c.Id select c;
上面實(shí)現(xiàn)了按照Id來(lái)進(jìn)行Linq排序??墒切枨笞兞耍脩衄F(xiàn)在想用Name來(lái)排序。好辦!把上面的改一改比如下面這樣就可以了:
- from c in Customers orderby c.Name select c;
可是需求又變了,用戶現(xiàn)在說(shuō),你在程序中不能寫死,得列一個(gè)菜單,我點(diǎn)哪個(gè)你就按哪個(gè)給我Linq排序。這個(gè)也不難辦:
- var searchResult = from c in Customers select c;
- if (columnName == "Id")
- {
- searchResult = from c in Customers orderby c.Id select c;
- }
- else if (columnName == "Name")
- {
- searchResult = from c in Customers orderby c.Id select c;
- }
- else ...
這確實(shí)解決了問(wèn)題,可是這樣的代碼不易維護(hù)。如果加屬性了怎么辦?如果屬性改名字了怎么辦?如果有好多的不同的類都需要這樣的Linq排序怎么辦?
下面我介紹一種較為通用的解決方案,此方案的核心技術(shù)是反射(Reflection)和擴(kuò)展方法(Extension Methods)。
- public static IOrderedQueryable
OrderBy ( - this IQueryable
source, - Expression
> keySelector, - IComparer
comparer - )
參數(shù)source是一個(gè)用來(lái)排序的對(duì)象,keySelector是用來(lái)取出用來(lái)Linq排序的鍵的函數(shù),comparer(比較器)用來(lái)比較取出的兩個(gè)鍵值。
對(duì)象的屬性類型可能多種多樣,而我們又不想為每種類型分別指定comparer(因?yàn)槟菢幼龅脑捯矊⑹且欢裪f else…)。變通一下思路,不管遇到哪種類型的屬性,我們都先把它的值放到一個(gè)共同的容器中,然后為這個(gè)容器寫一個(gè)comparer類。我們把類型判斷留到了這個(gè)comparer中,因?yàn)轭愋褪怯邢薜?,至少我們需要處理的那些屬性的類型是有限的?BR>上面提到的這個(gè)值的容器也是一個(gè)類,定義如下:
- public class CommonComparableValue
- {
- public object RealValue { get; set; }
- }
相應(yīng)的比較類定義如下:
- public class CommonComparableValueComparer : IComparer
- {
- public int Compare(CommonComparableValue x, CommonComparableValue y)
- {
- string s = x.RealValue as string;
- if (s != null)
- {
- return s.CompareTo(y.RealValue);
- }
- int? i = x.RealValue as int?;
- if (i != null)
- {
- return i.Value - (int)y.RealValue;
- }
- decimal? d = x.RealValue as decimal?;
- if (d != null)
- {
- return d.Value.CompareTo((decimal)y.RealValue);
- }
- throw new NotImplementedException("NotImplemented Data Type!!!");
- }
- }
這里的比較類只用到了int,string等幾種類型,如果屬性有其它的類型,也應(yīng)該在這里添加。從代碼實(shí)現(xiàn)可以看出,即使屬性的類型是復(fù)雜數(shù)據(jù)類型也可以這么處理。
現(xiàn)在來(lái)看keySelector的實(shí)現(xiàn)。它是用來(lái)取出待比較的屬性值的函數(shù)。這個(gè)函數(shù)應(yīng)該是這個(gè)樣子的:
- public delegate TResult Func
- ( T arg )
在這里,T的類型就是Customer,TResult就是剛剛已經(jīng)那個(gè)存放任意屬性類型的值的容器CommonComparableValue。
在這個(gè)實(shí)現(xiàn)取鍵值的函數(shù)里,我們只有一個(gè)Customer類型的參數(shù)arg可用,而那個(gè)用來(lái)Linq排序的屬性名字是在運(yùn)行期間確定的,如何才能取出我們想要的屬性的值呢?方法是這樣的,先通過(guò)擴(kuò)展方法為Customer加一個(gè)名為GetSortingKeyValue的取鍵值方法,代碼如下:
- public static class CustomerSortExtension
- {
- public static CommonComparableValue GetSortingKeyValue(this Customer ainfo, string columnName)
- {
- Type t = ainfo.GetType();
- PropertyInfo pinfo = t.GetProperty(columnName);
- if (pinfo == null)
- {
- throw new Exception("Property " + columnName + "not found");
- }
- else
- {
- return new CommonComparableValue
- {
- RealValue = pinfo.GetValue(ainfo, null)
- };
- }
- }
- }
這里就是通過(guò)一個(gè)字符串獲取屬性值,核心是反射。接下來(lái)只要在那個(gè)keySelector方法中調(diào)用GetSortingKeyValue方法就可以了。
到這里,各種準(zhǔn)備活動(dòng)就做完了?,F(xiàn)在來(lái)看一下怎么把這些東西組織起來(lái)實(shí)現(xiàn)Linq排序:
- var searchResult = from c in Customers select c;
- Func
myFunc = x => x.GetSortingKeyValue(columnName); - CommonComparableValueComparer comparer = new CommonComparableValueComparer();
- searchResult = searchResult.OrderBy(myFunc, comparer);
這種方案的主要內(nèi)容到這里就介紹完了。
***提一下,如果你想把這些東西用到你的代碼中,你一般需要做的只有:將擴(kuò)展方法的***個(gè)參數(shù)改為你需要Linq排序的那個(gè)類型。比如要為Person排序,擴(kuò)展方法則可以是這個(gè)樣子:
- public static CommonComparableValue GetSortingKeyValue(this Customer ainfo, string columnName)
當(dāng)然,那個(gè)CommonComparableValueComparer類也應(yīng)該根據(jù)實(shí)際類型修改以支持更多的屬性類型。
以上就是對(duì)Linq排序的詳細(xì)介紹,從理論到方法,很有價(jià)值的一篇文章呦!
【編輯推薦】