在建议10中我们创建了一个Salary对象,不妨回顾一下该建议中针对Salary的需求。首先,为了满足针对BaseSalary的排序要求,我们让类继承了IComparable<T>接口,实现了一个默认的CompareTo方法;其次,我们接到第二个需求,要为Salary针对Bonus排序。由于IComparable<T>接口只提供一个CompareTo方法,所以,不得不另外创建一个比较器,继而在调用Salary集合的Sort方法时,将这个比较器当做参数传进去。总而言之,如果存在这样一个Salary集合:
List<Salary>companySalary=new List<Salary>()
{
new Salary(){Name="Mike",BaseSalary=3000,Bonus=1000},
new Salary(){Name="Rose",BaseSalary=2000,Bonus=4000},
new Salary(){Name="Jeffry",BaseSalary=1000,Bonus=6000},
new Salary(){Name="Steve",BaseSalary=4000,Bonus=3000}
};
要满足上面所阐述的两个需求,就需要在客户端进行如下调用:
Console.WriteLine("BaseSalary排序:");
//对BaseSalary进行排序,调用默认的CompareTo方法
companySalary.Sort();
foreach(Salary item in companySalary)
{
Console.WriteLine(string.Format("Name:{0}\tBaseSalary:{1}\tBonus:{2}",
item.Name,item.BaseSalary,item.Bonus));
}
Console.WriteLine("Bonus排序:");
//提供一个非默认的比较器,调用BonusComparer的
//CompareTo方法
companySalary.Sort(new BonusComparer());
foreach(Salary item in companySalary)
{
Console.WriteLine(string.Format("Name:{0}\tBaseSalary:{1}\tBonus:{2}",
item.Name,item.BaseSalary,item.Bonus));
}
最终的排序输出为:
BaseSalary排序:
Name:Jeffry BaseSalary:1000 Bonus:6000
Name:Rose BaseSalary:2000 Bonus:4000
Name:Mike BaseSalary:3000 Bonus:1000
Name:Steve BaseSalary:4000 Bonus:3000
Bonus排序:
Name:Mike BaseSalary:3000 Bonus:1000
Name:Steve BaseSalary:4000 Bonus:3000
Name:Rose BaseSalary:2000 Bonus:4000
Name:Jeffry BaseSalary:1000 Bonus:6000
通过以上方式实现的排序至少存在两个问题:
1)可扩展性太低,如果存在新的排序要求,就必须实现新的比较器。
2)对代码的侵入性太高,为类型继承了接口,增加了新的方法。
那么,有没有一种方法,即使类型只存在自动实现的属性,也能满足多方面的排序要求呢?答案是使用LINQ。LINQ提供了类似于SQL的语法来实现遍历、筛选与投影集合的功能。借助于LINQ的强大功能,我们通过两条语句就能实现上述的排序要求,同时,保持类型Salary仅仅是一个最简单的实体类,代码如下所示:
static void Main(string[]args)
{
List<Salary>companySalary=new List<Salary>()
{
new Salary(){Name="Mike",BaseSalary=3000,Bonus=1000},
new Salary(){Name="Rose",BaseSalary=2000,Bonus=4000},
new Salary(){Name="Jeffry",BaseSalary=1000,Bonus=6000},
new Salary(){Name="Steve",BaseSalary=4000,Bonus=3000}
};
Console.WriteLine("默认排序:");
foreach(Salary item in companySalary)
{
Console.WriteLine(string.Format("Name:{0}\tBaseSalary:
{1}\tBonus:{2}",item.Name,item.BaseSalary,item.Bonus));
}
Console.WriteLine("BaseSalary排序:");
var orderByBaseSalary=from s in companySalary orderby s.BaseSalary select s;
foreach(Salary item in orderByBaseSalary)
{
Console.WriteLine(string.Format("Name:{0}\tBaseSalary:
{1}\tBonus:{2}",item.Name,item.BaseSalary,item.Bonus));
}
Console.WriteLine("Bonus排序:");
var orderByBonus=from s in companySalary orderby s.Bonus select s;
foreach(Salary item in orderByBonus)
{
Console.WriteLine(string.Format("Name:{0}\tBaseSalary:
{1}\tBonus:{2}",item.Name,item.BaseSalary,item.Bonus));
}
}
class Salary
{
public string Name{get;set;}
public int BaseSalary{get;set;}
public int Bonus{get;set;}
}
本段代码的输出将与本建议一开始的代码输出一致。
再来分析上面代码中隐含的一个功能,foreach实际隐含调用的是集合对象orderByBaseSalary和orderByBonus的迭代器。以往,如果我们要绕开集合的Sort方法对集合元素按照一定的顺序进行迭代,则需要让类型继承IEnumerable接口(泛型集合是IEnumerable<T>接口),实现一个或多个迭代器。现在从LINQ查询生成匿名类型来看,相当于可以无限为集合增加迭代需求。
当然,我们不禁要问,有了LINQ之后,我们是否就不再需要比较器和迭代器了呢?答案是否定的。我们可以利用LINQ的强大功能简化自己的编码,但是LINQ功能的实现本身就是借助于FCL泛型集合的比较器、迭代器、索引器的。LINQ相当于封装了这些功能,让我们使用起来更加方便。在命名空间System.Linq下存在很多静态类,这些静态类存在的意义就是为FCL的泛型集合提供扩展方法,如上面代码中的下面这条语句:
var orderByBaseSalary=from s in companySalary orderby s.BaseSalary select s;
orderby实际就是调用了System.Linq.Enumerable类型的OrderBy方法:
public static IOrderedEnumerable<TSource>OrderBy<TSource,TKey>(this
IEnumerable<TSource>source,Func<TSource,TKey>keySelector)
{
//省略
}
这是一个扩展方法,它为继承了IEnumerable<T>接口的集合类型提供排序的功能。
强烈建议你利用LINQ所带来的便捷性,但我们仍需掌握比较器、迭代器、索引器的原理,以便更好地理解LINQ的思想,写出更高质量的代码。