zoukankan      html  css  js  c++  java
  • LINQ之延迟执行标准查询操作符(上)

    标准查询操作符(Standard Query Operator)和查询表达式(Query Expression)是实现LINQ查询2种方式。

    通过查看IL代码,我们会发现查询表达式编译后也是转换成标准查询操作符的,并且有些查询时无法用查询表达式来操作的,

    因此标准查询操作符显得格外重要,我们将分几次介绍他们。

    大多数的标准查询操作符静态类:System.Linq.Enumerable的扩展方法,并且将IEnumerable作为其第一个参数。

    标准查询操作符包括两种类型:延迟执行和立即执行。我们将分别介绍他们。先介绍延迟执行吧。

    操作符:Where

    描述:用于包含/过滤数据

    类型:延迟执行

    原型:2种

    第一种原型:

    public static IEnumerable<TSource> Where<TSource>(
    	this IEnumerable<TSource> source,
    	Func<TSource, bool> predicate
    )

    这种原型有2个输入参数,但是由于这是一个扩展方法,因此事实上我们不用传如序列作为第一个参数,

    第二个参数是委托,委托的输入参数的类型跟枚举的数据项的类型是一样的,在这个例子中为TSource,

    我们可以使用lambda表达式,举个例子:

                int[] nums = new int[] { 4, 6, 7, 1, 0, 23, 5, 9, 12 };
    
                var evenNums = nums.Where(item => item%2 == 0);
    
                foreach (var evenNum in evenNums)
                {
                    Console.WriteLine(evenNum);
                }

    对比之前的where语句,是不是变得更简单了呢?

    第二种原型:

    public static IEnumerable<TSource> Where<TSource>(
    	this IEnumerable<TSource> source,
    	Func<TSource, int, bool> predicate
    )

    第二种原型跟第一种原型的唯一区别是,委托方法多了一个int类型的输入参数,该参数是一个索引,代表数据源的索引,跟C#中的大部分索引一样,都是基于0开始的索引。

    举个例子,还是上面的数据源,找出奇数位置上的数字:

                int[] nums = new int[] { 4, 6, 7, 1, 0, 23, 5, 9, 12 };
    
                var evenNums = nums.Where((p,i) => (i & 1) == 0);
    
                foreach (var evenNum in evenNums)
                {
                    Console.WriteLine(evenNum);
                }

    在这个例子中,我们没有用到元素p本身。

    操作符:Select

    描述:也称作投射,用于产生选择后的元素或者从原有序列中产生出新的元素序列,新的序列类型可能跟原有数据源的类型不一致

    类型:延迟执行

    原型:2种

    第一种原型:

    public static IEnumerable<TResult> Select<TSource, TResult>(
    	this IEnumerable<TSource> source,
    	Func<TSource, TResult> selector
    )
    

    这种原型接受一个输入数据源和选择器方法委托作为输入参数,同时返回一个新的,可能跟输入数据源元素类型不一样的对象,即TSource和TResult可能是不一样的。

    e.g.

                string[] allBrands = new string[] { "Exuviance", "Avene", "Baby Quasar", "Ecoya", "Alterna", "Ecru New York" };
    
                var brandsLength = allBrands.Select(b => b.Length);
    
                foreach (var length in brandsLength)
                {
                    Console.WriteLine(length);
                }

    在这个例子中,数据源是一个字符串数组,查询结果返回的是数组中每个字符串的长度。

    再举个例子:

                string[] allBrands = new string[] { "Exuviance", "Avene", "Baby Quasar", "Ecoya", "Alterna", "Ecru New York" };
    
                var namedBrands = allBrands.Select(b => new {b,b.Length});
    
                foreach (var namedBrand in namedBrands)
                {
                    Console.WriteLine("{0} length:{1}",namedBrand.b, namedBrand.Length);
                }

    这个例子中,返回的是一个新构建的对象。

    第二种原型:

    public static IEnumerable<TResult> Select<TSource, TResult>(
    	this IEnumerable<TSource> source,
    	Func<TSource, int, TResult> selector
    )

    跟Where操作符的第二种原型类似,委托方法的第二个参数依旧为数据源序列的0基索引。

    e.g.

                string[] allBrands = new string[] { "Exuviance", "Avene", "Baby Quasar", "Ecoya", "Alterna", "Ecru New York" };
    
                var namedBrands = allBrands.Select((b,i) => new { Index=i+1, b });
    
                foreach (var namedBrand in namedBrands)
                {
                    Console.WriteLine("{0}.{1}", namedBrand.Index, namedBrand.b);
                }

    借助索引,我们很容易知道各个品牌的位置。

    操作符:SelectMany

    描述:将数据源中的每个元素投射成一个IEnumerable(Of T)并将结果序列合并成一个序列

    类型:延迟执行

    原型:官方提供了4种,这里主要介绍2种

    第一种原型:

    public static IEnumerable<TResult> SelectMany<TSource, TResult>(
    	this IEnumerable<TSource> source,
    	Func<TSource, IEnumerable<TResult>> selector
    )

    这里我们借用msdn的例子来说明:

    class PetOwner
        {
            public string Name { get; set; }
            public List<String> Pets { get; set; }
        }
        PetOwner[] petOwners = 
                        { new PetOwner { Name="Higa, Sidney", 
                              Pets = new List<string>{ "Scruffy", "Sam" } },
                          new PetOwner { Name="Ashkenazi, Ronen", 
                              Pets = new List<string>{ "Walker", "Sugar" } },
                          new PetOwner { Name="Price, Vernette", 
                              Pets = new List<string>{ "Scratches", "Diesel" } } };
    
                // Query using SelectMany().
                IEnumerable<string> query1 = petOwners.SelectMany(petOwner => petOwner.Pets);
    
                // Only one foreach loop is required to iterate 
                // through the results since it is a
                // one-dimensional collection.
                foreach (string pet in query1)
                {
                    Console.WriteLine(pet);
                }
     
    相比之下,如果我们用Select方法:
     
        IEnumerable<List<String>> query2 =
                        petOwners.Select(petOwner => petOwner.Pets);
    
                // Notice that two foreach loops are required to 
                // iterate through the results
                // because the query returns a collection of arrays.
                foreach (List<String> petList in query2)
                {
                    foreach (string pet in petList)
                    {
                        Console.WriteLine(pet);
                    }
                    Console.WriteLine();
                }
     
    首先,返回的结果集是不一样的,我们执行查询的时候,步骤也是不一样的。
     
    第二种原型:
     
    public static IEnumerable<TResult> SelectMany<TSource, TResult>(
    	this IEnumerable<TSource> source,
    	Func<TSource, int, IEnumerable<TResult>> selector
    )
    

    跟之前的第二种原型类似,不再举例。

     

    操作符:Take

    描述:从序列中返回开始的临近几个元素

    类型:延迟执行

    原型:一种

     

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

     

    e.g.

     

    int[] nums = new int[] { 4, 6, 7, 1, 0, 23, 5, 9, 12 };
    
    var evenNums = nums.Take(3);

     

    操作符:TakeWhile

    描述:返回一个序列中的元素直到某个条件成立

    类型:延迟执行

    原型:2种

     

    第一种原型:

    public static IEnumerable<TSource> TakeWhile<TSource>(
    	this IEnumerable<TSource> source,
    	Func<TSource, bool> predicate
    )
     
    举个例,我们要从一个int类型的数组中找出所有的元素,直到我们第一次遇到0
     
    int[] nums = new int[] { 4, 6, 7, 1, 0, 23, 5, 9, 12 };
    
                var evenNums = nums.TakeWhile(item => item != 0);
    
                foreach (var evenNum in evenNums)
                {
                    Console.WriteLine(evenNum);
                }

    输出结果为:

    4

    6

    7

    1

    显然,当遇到0时,查询结束

    第二种原型增加一个索引参数,不再赘述。

     

    操作符:Skip

    描述:从一个序列中跳过指定数目的元素,注意,是从头开始跳过的,因此使用此方法与排序有关。

    类型:延迟执行

    原型:一种

     

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

    e.g.

                int[] nums = new int[] { 4, 6, 7, 1, 0, 23};
    
                var evenNums = nums.Skip(3);
    
                foreach (var evenNum in evenNums)
                {
                    Console.WriteLine(evenNum);
                }

    输出结果为:

    1

    0

    23

    即,跳过了刚开始的三个数字

    操作符:SkipWhile

    描述:跳过所有的元素直到某个指定的条件不再成立,并返回剩下的元素

    类型:延迟执行

    原型:2种

    原型一:

    public static IEnumerable<TSource> SkipWhile<TSource>(
    	this IEnumerable<TSource> source,
    	Func<TSource, bool> predicate
    )
    

    e.g.

    int[] nums = new int[] { 4, 6, 7, 1, 0, 23, 5, 9, 12 };
    
                var evenNums = nums.SkipWhile(item => item<10);
    
                foreach (var evenNum in evenNums)
                {
                    Console.WriteLine(evenNum);
                }

    该例子返回的结果为:

    23

    5

    9

    12

    因为,当遇到23时,item<10已经不再成立,因此Skip停止,并返回剩下的所有的元素

    原型二与前类似。

    参考:《Pro.LINQ.Language.Integrated.Query.in.Csharp.2010》

  • 相关阅读:
    【浅谈数位计数一类问题】
    2.2、idea 中写spark sql
    javascript事件之鼠标滚轮(mousewheel)和DOMMouseScroll事件
    JS 判断鼠标滚轮的上下滚动
    gulp 用法 小结
    'gulp'不是内部或者外部命令,也不是可运行的程序
    Gulp的安装与配置
    前端构建工具gulpjs的使用介绍及技巧
    【转】弧度和角度的转换
    前端构建工具gulpjs的使用介绍及技巧
  • 原文地址:https://www.cnblogs.com/tian2010/p/2399513.html
Copyright © 2011-2022 走看看