“21天好习惯” 第一期-10

LINQ 方法语法

书写LINQ查询时又两种语法可供选择:方法语法(Fluent Syntax)和查询语法(Query Expression)。

链接查询运算符

如下例:查询出所有含有字母”a”的姓名,按长度进行排序,然后把结果全部转换成大写格式。

        static void Main(string[] args)
        {
            string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" };

            IEnumerable<string> query = names
                .Where(n => n.Contains("a"))
                .OrderBy(n => n.Length)
                .Select(n => n.ToUpper());
 
            foreach (string name in query) Console.WriteLine(name);
        }
 
        // Result:
        JAY
        MARY
        HARRY

就像在本例中所示,当链接使用查询运算符时,一个运算符的输出sequence会成为下一个运算符的输入sequence,其结果形成了一个sequence的传输链,如图所示:

 图:链接查询运算符

Where, OrderyBy, Select这些标准查询运算符对应Enumerable类中的相应扩展方法。Where产生一个经过过滤的sequence;OrderBy生成输入sequence的排序版本;Select得到的序列中的每个元素都经过了给定lambda表达式的转换。

下面是Where, OrderBy, Select这几个扩展方法的签名:

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

        public static IEnumerable<TSource> OrderBy<TSource, TKey>
            (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)

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

扩展方法的重要性

扩展方法非常自然地反映了从左到右的数据流向,同时保持lambda表达式与查询运算符的位置一致性。

            //   extension methods make LINQ elegant
            IEnumerable<string> query = names
                .Where(n => n.Contains("a"))
                .OrderBy(n => n.Length)
                .Select(n => n.ToUpper());
 
            //   static methods lose query's fluency
            IEnumerable<string> query2 =
                Enumerable.Select(
                    Enumerable.OrderBy(
                        Enumerable.Where(names, n => n.Contains("a")
                        ), n => n.Length
                    ), n => n.ToUpper()
                );

创建Lambda表达式

在上例中,我们向Where运算符提供了如下Lambda表达式:n => n.Contains(“a”)。对各个查询运算符来说,Lambda表达式的目的不尽相同。对于Where,它决定了一个element是否包含在结果sequence中;对于OrderBy,它把每个element映射到比较的键值;而对于Select,lambda表达式则决定了输入sequence中的元素要怎么样的转换再放入输出sequence中。

        public static IEnumerable<TSource> Where<TSource>(
            this IEnumerable<TSource> source, Func<TSource, bool> predicate)
        {
            foreach (TSource element in source)
                if (predicate(element))
                    yield return element;
        }

标准查询运算符使用了通用的Func委托,Func是一组定义在System.Linq的命名空间中的通用委托。它接受一系列的输入参数和一个返回值,返回值对应最后一个参数定义。所以,Func<TSource, bool>委托匹配 TSource => bool表达式,接受TSource输入参数,返回一个bool值。

Lambda表达式和元素类型

标准查询运算符使用统一的类型名称:

通用类型名称

用途

TSource

Input sequence中的element类型

TResult

Output sequence中的element 类型(如果和TSource不相同)

TKey

使用sorting、grouping、joining等的键值类型

TSource由输入sequence决定,而TResult和TKey则从我们提供的Lambda表达式推断得到。

Func<TSource,TResult>匹配TSource => TResult的Lambda表达式,接受一个输入参数TSource,返回TResult。因为TSource和TResult是不同的类型,所以我们的Lambda表达式甚至可以改变输入element的数据类型。下面的示例就把string类型元素转换为int类型元素:

        static void TestSelectOperator(){
            string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" };
            // 编译器将会从Lambda表达式 n => n.Length推断出TResult为int类型
            IEnumerable<int> query = names.Select(n => n.Length);
 
            foreach (int length in query)
                Console.Write(length + "|");    //  3|4|5|4|3
        }

而对Where查询运算符来讲,它并不需要对输出element进行类型推断,因为它只是对输入elements进行过滤而不作转换,因此输出element和输入element具有相同的数据类型。

对于OrderBy查询运算符来讲,Func<TSource, TKey>把输入元素映射至一个排序键值。TKey由Lambda表达式的结果推断出来,比如我们可以按长度或按字母顺序对names数组进行排序:


            string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" };
            IEnumerable<string> sortedByLength, sortedAlphabetically;
            sortedByLength = names.OrderBy(n => n.Length);  // int key
            sortedAlphabetically = names.OrderBy(n => n);   // string key

其他查询运算符

并不是所有的查询运算符都返回一个sequence。元素(element)运算符会从输入sequence中获取单个元素,如:First,Last和ElementAt:

            int[] numbers = { 10, 9, 8, 7, 6 };  
            int firstNumber = numbers.First();                  // 10
            int lastNumber = numbers.Last();                    // 6
            int secondNumber = numbers.ElementAt(1);            // 9
            int lowestNumber = numbers.OrderBy(n => n).First(); // 6

            int count = numbers.Count();    // 5
            int min = numbers.Min();        // 6

            bool hasTheNumberNine = numbers.Contains(9);    // true
            bool hasElements = numbers.Any();               // true
            bool hasAnOddElement = numbers.Any(n => (n % 2) == 1);  //true

            int[] seq1 = { 1, 2, 2, 3 };
            int[] seq2 = { 3, 4, 5 };
            IEnumerable<int> concat = seq1.Concat(seq2);    // { 1, 2, 2, 3, 3, 4, 5 }
            IEnumerable<int> union = seq1.Union(seq2);      // { 1, 2, 3, 4, 5 }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值