C#之LINQ——.NET新特性(叁)

目录

目录

八、NET 6的新特性

1、MaxBy 和 MinBy

按照给定条件找出集合中的最大或最小值

比如找出集合中最重的宠物,以前需要OrderBy + Last

pets.OrderBy(pet => pet.Weight).Last();

现在可以直接使用MaxBy,根据体重找到集合的最大值:

pets.MaxBy(pet => pet.Weight);

2、DistinctBy

根据给定条件去除集合的重复项

比如我们有人员集合,其中判断是否重复的条件是Id,则可以这样去重:

var people = new[]
{
    new Person(1, "John", 1982),
    new Person(1, "john", 1982),
    new Person(2, "Monica", 1994),
    new Person(2, "MONICA", 1994),
    new Person(3, "Peter", 2000),
};

var ans = people.DistinctBy(person => person.Id);

结果就是相同Id的数据只会保留一个。

3、Chunk

将集合拆分为等大小的块。比如用于某些批处理操作。

比如将数组拆分为每批大小为3的块:

var numbers = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var chunks = numbers.Chunk(3);

结果会是一个二维集合,集合的每个元素是一个int数组,数组大小为3。除了最后一个元素,因为最后一批不满3个元素。

4、Zip

以前使用Zip连接多个集合必须使用多次,而 .NET 6 中,Zip方法可以接收第二个参数,从而合并3个集合。

5、从末尾开始的索引操作符

以前使用ElementAt只能访问正序遍历的指定位置的元素,如果想访问倒数的元素,比如倒数第3个元素,则需要这样的操作:

numbers.ElementAt(numbers.Count() - 3)

而在 .NET 6 中,我们可以使用^操作符来表示倒序遍历的指定位置的元素:

numbers.ElementAt(^3);

6、范围操作符

以前获取集合中指定范围的元素时,比如提取从2到6位的元素,需要这样做:

numbers.Skip(2).Take(4);

而在 .NET 6中,可以使用..操作符来表示提取元素的范围:

numbers.Take(2..6);

可以省略第二个参数,表示获取给定索引之后的所有元素;或者省略第一个参数,表示获取给定索引之前的所有元素:

numbers.Take(6..); // 第6位之后的所有元素
numbers.Take(..4); // 前4个元素,不过很少用

..操作符可以与^操作符一起使用,比如获取倒数第三个数字之前的所有元素(不包含倒数第3个):

numbers.Take(..^3);

7、TryGetNonEnumeratedCount

这个意思是尝试在不枚举(遍历)集合的情况下获取计数。我们知道,LINQ不仅可以操作内存中的集合,还可以查询数据库和其他远程数据源。某些情况下,在内存中看似简单的操作,在数据库端执行却可能非常耗时。

比如下面这个简单例子,将两个集合连接起来,并计算集合数量。

由于这两个集合是数组,因此可以很方便的获取到两个数组的长度(Length属性),无需枚举(遍历)集合。但如果这些集合不是数组而是数据库中的表,那么请求计数会是一个相当繁重的操作。

IEnumerable<Pet> pets1 = new[]
{
    new Pet(1, "Hannibal", PetType.Fish, 1.1f),
    new Pet(2, "Anthony", PetType.Cat, 2f),
    new Pet(3, "Ed", PetType.Cat, 0.7f),
};

IEnumerable<Pet> pets2 = new[]
{
    new Pet(6, "Lucky", PetType.Dog, 5f),
    new Pet(7, "Storm", PetType.Cat, 0.9f),
    new Pet(8, "Nyan", PetType.Cat, 2.2f)
};
var count = pets1.Concat(pets2).Count();

如果我们有一个IEnumerable集合,但不知道具体是什么,我们只想在不枚举集合的情况下获取这个集合的计数。那么可以使用TryGetNonEnumeratedCount方法,如果获取计数需要枚举集合,则不提供值;反之则可以获取计数,该方法返回值是bool类型的值。

比如可以这样使用:

if(numbers.TryGetNonEnumeratedCount(out int safeCount))
{
    Console.WriteLine("Count is: " + safeCount);
}
else
{
    Console.WriteLine("Can't get the count without enumerating.");
}

九、NET 9的新特性

1、CountBy

按照给定的条件分组并统计各个组中的元素数量

比如统计宠物集合中每种类型宠物的数量,以前必须GroupBy + Select + Count

var petCountsByTypeOld = pets
    .GroupBy(pet => pet.PetType)
    .Select(group => new { Type = group.Key, Count = group.Count() });

而到了 .NET 9 则可以直接使用CountBy

var petCountsByType = pets.CountBy(pet => pet.PetType);

返回值会是键值对的集合,其中这里键是宠物类型,值则是计数。

2、AggregateBy

按键分组并对每个分组执行聚合操作。和CountBy类似,该方法消除了在对分组数据执行操作时使用GroupBy的需要。

比如统计每种类型的宠物总重量,以前需要这样做:

var totalWeightByTypeOld = pets
     .GroupBy(pet => pet.PetType)
     .Select(group => new { Type = group.Key, TotalWeight = group.Sum(pet => pet.Weight) });

而 .NET 9 可以使用AggregateByy

var totalWeightByType = pets.AggregateBy(
    pet => pet.PetType,
    0f,
    (total, pet) => total + pet.Weight
);

其中第一个参数是键选择器,即分组标准;第二个参数则是Seed,累加结果的初始值;第三个参数则是聚合函数。