LINQ中的陷阱--TakeWhile&SkipWhile

在用TakeWhile,SkipWhile设置陷阱之前,我们先来看一看他们的兄弟Take和Skip:

public static IEnumerable<T> Take<T>(IEnumerable<T> source, int count)

public static IEnumerable<T> Skip<T>(IEnumerable<T> source, int count)

这两个操作符从字面上看就能理解其含义.Take将枚举出source中的前count个元素,返回给客户端.而Skip 则恰好相反,将跳过source中的前count个元素,枚举其余元素.LINQ内部实现代码十分简单,不用多分析.不过,他们的兄弟TakeWhile 和SkipWhile确埋下了个陷阱,让我小小的摔了一跤.现在,我就来重新布置这个陷阱:

考虑如下的数据源:

static List<Customer> customers = new List<Customer> {
new Customer { CustomerID=1,Name="woody1"},
new Customer { CustomerID=2,Name="woody2"},
new Customer { CustomerID=3,Name="woody3"},
new Customer { CustomerID=4,Name="woody1"}
};

 在这个数据源的基础上,我进行了如下操作:

var cs1 = customers.TakeWhile(c => c.Name == "woody1");
var cs2 = customers.TakeWhile(c => c.Name == "woody2");
var cs3 = customers.SkipWhile(c => c.Name == "woody1");
var cs4 = customers.SkipWhile(c => c.Name == "woody2");

好了.现在,你能猜得出来cs1--cs4这四个IEnumerable<Customer>变量中都保存着些什么什么元素吗? 
  正确答案是: 
  cs1 : woody1(CustomerID=1) 
  cs2 : 没有任何元素 
  cs3 : woody2 , woody3 , woody1(CustomerID=4) 
  cs4 : woody1(CustomerID=1),woody2,woody3,woody1(CustomerID=4) 
  Surprise?:)反正我是小小的"惊喜"了一下.OK.研究实现代码吧... 
  TakeWhile在LINQ中实现的思想是:对数据源进行枚举,从第一个枚举得到的元素开始,调用客户端传入的predicate( c.Name == ""woodyN"),如果这个predicate委托返回true的话,则将该元素作为Current元素返回给客户端,并且,继续进行相同的枚举,判断操作.但是,一旦predicate返回false的话,MoveNext()方法将会返回false,枚举就此打住,忽略剩下的所有元素. 
类似的,SkipWhile也对数据源进行枚举,从第一个枚举得到的元素开始,调用客户端的predicate,如果返回true,则跳过该元素,继续进行枚举操作.但是,如果一旦predicate返回为false,则该元素以后的所有元素,都不会再调用predicate,而全部枚举给客户端.

这两个方法总结为:遍历时,检查predicate条件,只要一遇到返回false,就打住,后面的元素不再去检测,直接返回结果。
  (内部实现代码很简单,不再列出) 
  现在,再回头看看陷阱的正确答案,是不是跑出来了呢?:)最开始,我一直以为是LINQ的一个BUG,还打算上LINQ论坛报BUG,不过,后来细想 Take,Skip,再详细阅读了LINQ的文档后,发现似乎这并不是BUG,这就是这两个操作符的正确逻辑.不过,起这样的名字,出这样的结果,实在让人觉得困惑啊~

完整的控制台程序代码如下:

class Customer
    {
        public int CustomerID { get; set; }
        public string Name { get; set; }
    }

class Program
    {
        static void Main(string[] args)
        {
            List<Customer> customers = new List<Customer> {
                new Customer { CustomerID=1,Name="woody1"},
                new Customer { CustomerID=2,Name="woody2"},
                new Customer { CustomerID=3,Name="woody3"},
                new Customer { CustomerID=4,Name="woody1"}
            };      

            var cs1 = customers.TakeWhile(c => c.Name == "woody1");
            var cs2 = customers.TakeWhile(c => c.Name == "woody2");
            var cs3 = customers.SkipWhile(c => c.Name == "woody1");
            var cs4 = customers.SkipWhile(c => c.Name == "woody2");

            Console.WriteLine("Result One:TakeWhile(c => c.Name == woody1)");
            foreach (var customer in cs1)
            {
                Console.WriteLine(customer.CustomerID + ":" + customer.Name);
            }
            Console.WriteLine("Result Two:TakeWhile(c => c.Name == woody2)");
            foreach (var customer in cs2)
            {
                Console.WriteLine(customer.CustomerID + ":" + customer.Name);
            }
            Console.WriteLine("Result Three:SkipWhile(c => c.Name == woody1)");
            foreach (var customer in cs3)
            {
                Console.WriteLine(customer.CustomerID + ":" + customer.Name);
            }
            Console.WriteLine("Result Four:SkipWhile(c => c.Name == woody2)");
            foreach (var customer in cs4)
            {
                Console.WriteLine(customer.CustomerID + ":" + customer.Name);
            }
            Console.ReadKey();

        }
时间: 2024-11-07 04:47:11

LINQ中的陷阱--TakeWhile&SkipWhile的相关文章

Linq中Take、TakeWhile、Skip、SkipWhile的比较(转)

参考文章:http://blog.csdn.net/lxfzgg/article/details/20534281 Take() int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 }; var first3Numbers = numbers.Take(3); //从第一个元素开始,获取三个 return的是前面的数 Console.WriteLine("First 3 numbers:"); foreach (var n in first3N

Linq中的TakeWhile和SkipWhile的用法

Linq中的SkipWhile 1.含义 (1).对数据源进行枚举,从第一个枚举得到的元素开始,调用客户端的predicate (2).如果返回true,则跳过该元素,继续进行枚举操作. (3).但是,如果一旦predicate返回为false,则该元素以后的所有元素,都不会再调用predicate,而全部枚举给客户端. 2.实例             int[] grades = { 59, 82, 70, 56, 92, 98, 85 };             IEnumerable<

LINQ中的一些查询语句格式

LINQ的基本格式如下所示:var <变量> = from <项目> in <数据源> where <表达式> orderby <表达式> LINQ 基本子句from查询子句——基础后面跟随着项目名称和数据源示例代码如下:var str = from lq in str select lq; 其中select语句指定了返回到集合变量中的元素是来自哪个数据源的 from查询子句——嵌套查询可以在from子句中嵌套另一个from子句即可,示例代码如下

Sliverlight linq中的数组筛选数据库中的数据

首先 什么是linq呢 ? LINQ即Language Integrated Query(语言集成查询),LINQ是集成到C#和Visual Basic.NET这些语言中用于提供查询数据能力的一个新特性. 它是.NET框架的扩展,它允许我们以数据库查询的方式查询数据集合. 借助于LINQ技术,我们可以使用一种类似SQL的语法来查询任何形式的数据. 接下来讲讲我工作中用到的 linq中的数组筛选数据库中的数据 public List GetList1(string cardPhone,string

Linq中GroupBy方法的使用总结(转)

Group在SQL经常使用,通常是对一个字段或者多个字段分组,求其总和,均值等. Linq中的Groupby方法也有这种功能.具体实现看代码: 假设有如下的一个数据集: public class StudentScore { public int ID { set; get; } public string Name { set; get; } public string Course { set; get; } public int Score { set; get; } public str

Linq 中按照多个值进行分组(GroupBy)

Linq 中按照多个值进行分组(GroupBy) .GroupBy(x => new { x.Age, x.Sex }) group emp by new { emp.Age, emp.Sex } into g // 实现多key分组的扩展函数版本 var sums = empList .GroupBy(x => new { x.Age, x.Sex }) .Select(group => new { Peo = group.Key, Count = group.Count() });

linq中AsEnumerable和AsQueryable的区别

本文导读:用Linq来操作集合的时候会用到AsQueryable()和AsEnumerable(),何时该用AsQueryable()和何时该用AsEnumerable(),或许存在些疑惑.AsQueryable是在数据库中查询再返回数据,AsEnumerable是从数据库读取全部数据再在程序中查询. 在使用LINQ 进行数据集操作时,LINQ 不能直接从数据集对象中查询,因为数据集对象不支持LINQ 查询,所以需要使用AsEnumerable 方法返回一个泛型的对象以支持LINQ 的查询操作.

Entity Framework 6 Recipes 2nd Edition(11-9)译 -&gt; 在LINQ中使用规范函数

11-9. 在LINQ中使用规范函数 问题 想在一个LINQ查询中使用规范函数 解决方案 假设我们已经有一个影片租赁(MovieRental )实体,它保存某个影片什么时候租出及还回来,以及滞纳金等,如Figure 11-9. 所示: Figure 11-9. The MovieRental entity that has the dates for a rental period along with any late fees 我们想取得所有租期超过10天的影片 如何创建和使用查询,如Lis

04 LINQ中的聚合函数(Aggregate function in LINQ)

试想一下,我们有一个List<int>数组,现在我们想对List中的所有值求和.在没学习LINQ之前我们会轻松的写出下面的代码: static void Main(string[] args) { List<int> array = new List<int>() { 1, 3, 3, 2, 7, 3, 2, 8, 5, 4, 6 }; int sum = 0; foreach (int item in array) { sum += item; } Console.W