购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

建议30:使用LINQ取代集合中的比较器和迭代器

在建议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的思想,写出更高质量的代码。 X9s30+UOD01vUZsTnVQNCMxuNcSUn4hJIKXMcqAP5nCMvU9vrnT3av5rbAc7c0gd

点击中间区域
呼出菜单
上一章
目录
下一章
×

打开