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,累加结果的初始值;第三个参数则是聚合函数。