原文起始连接:https://dotnettutorials.net/course/linq/
说明:翻译接上篇,上一篇翻译了LINQ中的连接操作(Join,包括InnerJoin,GroupJoin,LeftJoin,CrossJoin)、元素操作(ElementAt、ElementAtOrDefault、First、FirstOrDefault、Last、LastOrDefault、Single、SingleOrDefault、DefaultIfEmpty)以及SequenceEqual。本篇继续介绍LINQ中的其他操作。
LINQ中的切分操作
切分操作用于将数据源集合切割成两部分,返回指定一部分且不改变元素的位置。Linq中主要提供以下几个方法来切分数据源。这些操作主要用于数据分页。
- Take 需要选择数据源的前n条数据
- Skip 选择除去前n条数据的剩余所有数据(即跳过前n条数据)
- TakeWhile 需要选择数据源数据从满足指定条件的元素开始
- SkipWhile 跳过指定条件的数据,返回剩余的所有数据
Take 与 Skip
这两个方法比较简单且相似,直接举例说明这两个方法使用。注意当数据源为null时使用Take和Skip将引发异常。
1 | List<int> numbers = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; |
TakeWhile 与 SkipWhile
这两个方法用法相似,传递一个条件断言即可。类似于Where,但需要注意的是,TakeWhile是当符合条件时的数据,遇到一个不符合条件的元素,则停止即便该元素后仍然有符合条件的元素也不再返回了。而Where的条件断言则针对数据源 所有元素。下面看例子
1 | List<int> numbers = new List<int>() { 1, 2, 3, 6, 7, 8, 9, 10, 4, 5 }; |
分页操作
使用Linq进行分页,优点是只需要向数据库必须的数据,减少数据在网络中的传输。但缺点是,需要向数据库提交的请求次数将增加。可以通过一次请求将所有数据缓存到客户端,再进行分页操作。Linq分页的公式如下:
1
2
3int pageNumer;
int pageSize;
var Result = DataSuource.Skip((pageNumer - 1) * pageSize).Take(pageSize);
产生操作(非扩展方法)
前面介绍的都是Linq提供的扩展方法,Enumerable类提供下面三个非扩展方法
- Range()
- Repeat
() - Empty
()
这三个方法使用一个简单的表达式生成指定类型的数组、序列或集合,而不需要用循环来产生。返回的结果实现IEnumerable接口。
由于返回的结果实现IEnumerable接口的,因此可以在Range()方法后使用先前介绍过的所有扩展方法(Select,Where等)。 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17IEnumerable<int> EvenNumbers = Enumerable.Range(10,30).Where(x => x%2 == 2);
IEnumerable<string> rangewithString = Enumerable.Range(1, 2).Select(x => (x * x) + " " + CustomLogic(x)).ToArray();
private static string CustomLogic(int x)
{
string result = string.Empty;
switch (x)
{
case 1:
result = "1st";
break;
case 2:
result = "2nd";
break;
}
return result;
}
Repeat是直接产生指定类型元素指定数字次,看下面的例子
1 | IEnumerable<string> repeatStrings = Enumerable.Repeat("Welcome to DOT NET Tutorials", 10); |
Empty方法产生一个空的数据集,为什么需要这个方法来生成一个空的数据集呢?当我们在准备遍历一个方法返回的数据源时,该数据源有可能是null,如果我们直接遍历,则会产生异常。此时,我们就可以使用空值合并访问运算符来给null值一个默认空的集合,而不是null值来引发异常。
1 | var emptyCollection1 = Enumerable.Empty<string>(); |
LINQ中的Append 和 Prepend
Append方法用于将一个元素添加到一个指定的数据源序列中。Append方法不修改序列的元素,因此它用原来的序列和新添加的元素创建一个序列的副本。Prepend方法和Append一样,它在序列前添加一个元素。看下Append和Prepend的签名:
1 | public static IEnumerable<TSource> Append<TSource>(this IEnumerable<TSource> source, TSource element); |
下面举例子
1 | List<int> intSequence = new List<int> {10, 20, 30, 40}; |
LINQ中的Zip方法
先看看Zip方法的签名:
1 | public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst,TSecond,TResult> resultSelector); |
Zip方法允许我们将两个数据源序列中的元素按照传入的方法合并,返回一个新的序列,该新的序列中的元素是原先两个序列的元素合并后的值。如果源数据序列长度不一致,则返回结果序列长度为两个中较短的那个序列的长度。
1 | int[] numbersSequence = { 10, 20, 30, 40, 50 }; |
延迟加载与立即执行
这个话题又被提起了,按照原文的顺序,这一节应该介绍一下这两个概念。因为后面的ToList、ToArray方法都会立即执行Linq查询。因此,有必要在此说明一下。
Linq查询操作符共有两类:
- 延迟或懒加载类: 这类查询操作符使用延迟执行,比如:Select、SelectMany、Where、Take、Skip等
- 即时加载类:这类查询操作符使用立即执行,比如:Count、Average、Min、Max、First、Last、ToArray、ToList等。
下面举例延迟加载,是指Linq的查询并不是在查询语句完成后,而是在我们去使用查询结果,比如遍历查询结果。
1 | public class Employee |
延迟加载的好处
- 避免不必要的查询执行从而提高应用效率
- 查询的创建和执行解耦,因此我们可以分几步来定义查询
- 当我们重新遍历时,Linq延迟执行通常重新计算,因此我们获得的总是最新数据
ToList和ToArray
ToList方法将System.Collections.Generic.IEnumerable<T>
转换成一个System.Collections.Generic.List<T>
集合。ToArray则是将System.Collections.Generic.List<T>
集合中的元素复制到一个新的数组。下面举例演示两个方法。
1 | int[] numbersArray = { 10, 22, 30, 40, 50, 60 }; |
ToDictionary
该方法将System.Collections.Generic.IEnumerable<T>
转换成一个System.Collections.Generic.Dictionary<TKey,TValue>
,根据一个具体的键选择器。看看这个方法的签名:
1 | public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector); |
下面举例
1 | List<Product> listProducts = new List<Product> |
Cast操作
Cast是将一个集合转换成另一个指定类型的集合,新集合中的元素还是原来集合中的元素。
1 | ArrayList list = new ArrayList{ 10, 20,30 }; |
Cast与OfType的区别:
Cast将会转换列表中的所有元素,如果有元素无法进行类型转换,则会引发异常(InvalidCastException)。而根据前面的介绍,OfType则是返回所有符合指定类型的元素,而不进行类型转换。
- 本文作者: 达文西
- 本文链接: https://edsiongithub.github.io/2022/04/01/34/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!