本章前面提到,查询可以推迟到访问数据项时再执行。
在迭代中使用查询,查询会执行。而使用转换操作符会立即执行查询,把结果放在数组、列表或字典中。
在下面的例子中,调用ToList()扩展方法,立即执行查询,把结果放在List<T>中:
private static void Conversion() { // query executed immediately List<Racer> racers = (from r in Formula1.GetChampions() where r.Starts > 150 //参赛超过150次的选手 orderby r.Starts descending select r).ToList(); //注意先括号再使用.ToList() foreach (var racer in racers) { Console.WriteLine("{0} {0:S}", racer); } }
把返回的对象放在列表中并没有这么简单。
例如,对于集合中从赛车到赛手的快速访问,可以使用新类Lookup<TKey, TElement>。
提示:Dictionary<TKey, TValue>只支持一个键对应一个值。
在System.Linq 命名空间的类Lookup<TKey,TElement>中,一个键可以对应多个值。这些类详见第10 章。
使用复合的from 查询,可以摊平赛手和赛车序列,创建带有Car 和Racer 属性的匿名类型。这句最关键
在返回的Lookup 对象中,键的类型应是表示汽车的string,值的类型应是Racer。
为了进行这个选择,可以给ToLookup()方法的一个重载版本传送一个键和一个元素选择器。
键选择器表示Car 属性,元素选择器表示Racer 属性。
代码的执行顺序是这样的:
private static void Conversion() { ILookup<string, Racer> racers = (from r in Formula1.GetChampions()//获得每一个赛手 from c in r.Cars //获得赛手的车子集合,遍历是每一个车子多次获得结果 select new { Car = c, //车子名 Racer = r //这个车手,如果这个车手有多部车子就会出现多个车子名,同一个车手对象的结果,然后 }).ToLookup(cr => cr.Car, cr => cr.Racer);//用车子名做Key,向里面添加车手,如果存在的Key,就会添加,不存在的Key,就建立了Key再添加 //最后的结果,就是获得了一个以所有车子为Key,对应Value是使用过此车子的车手的集合列表 if (racers.Contains("Williams")) { foreach (var williamsRacer in racers["Williams"])//访问Key是Williams的赛手集合,遍历 { Console.WriteLine(williamsRacer); // Racer 的 toString(); 方法结果 } } }用 Lookup 类的索引器访问的所有Williams 冠军如下:
Alan Jones
Keke Rosberg
Nigel Mansell
Alain Prost
Damon Hill
Jacques Villeneuve
如果需要在未类型化的集合上使用LINQ 查询,
例如ArrayList,就可以使用Cast()方法,映射成某个类型。
在下面的例子中,基于Object 类型的ArrayList 集合用Racer 对象填充。
为了定义强类型化的查询,可以使用Cast()方法。
private static void Conversion() { System.Collections.ArrayList list = new System.Collections.ArrayList(Formula1.GetChampions() as System.Collections.ICollection); var query = from r in list.Cast<Racer>() where r.Country == "USA" orderby r.Wins descending select r; foreach (var racer in query) { Console.WriteLine("{0:A}", racer); } }
冯瑞涛