.NET 6 Linq 的新功能来到最终回合。
本集提要
- 框架 : .NET 6
- 功能 : DistinctBy、ExceptBy、IntersectBy 和.UnionBy
方法宣告
在 .NET6 中,这四个方法分别有两个多载:
DistinctBy
- IEnumerable<TSource> DistinctBy<TSource,TKey> (this IEnumerable<TSource> source, Func<TSource,TKey> keySelector)
- IEnumerable<TSource> DistinctBy<TSource,TKey> (this IEnumerable<TSource> source, Func<TSource,TKey> keySelector, IEqualityComparer<TKey>? comparer)
ExceptBy
- IEnumerable<TSource> ExceptBy<TSource,TKey> (this IEnumerable<TSource> first, IEnumerable<TKey> second, Func<TSource,TKey> keySelector)
- IEnumerable<TSource> ExceptBy<TSource,TKey> (this IEnumerable<TSource> first, IEnumerable<TKey> second, Func<TSource,TKey> keySelector, IEqualityComparer<TKey>? comparer)
IntersectBy
- IEnumerable<TSource> IntersectBy<TSource,TKey> (this IEnumerable<TSource> first, IEnumerable<TKey> second, Func<TSource,TKey> keySelector);
- IEnumerable<TSource> IntersectBy<TSource,TKey> (this IEnumerable<TSource> first, IEnumerable<TKey> second, Func<TSource,TKey> keySelector, IEqualityComparer<TKey>? comparer)
UnionBy
- IEnumerable<TSource> UnionBy<TSource,TKey> (this IEnumerable<TSource> first, IEnumerable<TSource> second, Func<TSource,TKey> keySelector)
- IEnumerable<TSource> UnionBy<TSource,TKey> (this IEnumerable<TSource> first, IEnumerable<TSource> second, Func<TSource,TKey> keySelector, IEqualityComparer<TKey>? comparer);
Distinct vs DistinctBy
我们先瞧瞧 Distinct 替代关係,就 DistinctBy<TSource,TKey>(IEnumerable<TSource>, Func<TSource,TKey>) 这个多载而言,很明显在替代 Distinct 中带有 IEqualityComparer<TSource> 的多载,举个简单的例子,如果我有以下字串阵列:
 string[] colors = { "red", "green", "blue", "green", "yellow", "blue" };如果想要取得不重複的字串序列,你应该会这么用:
var distinctColors = colors.Distinct();.NET 6 以后你也可以这么用:
 var distinctColors = colors.DistinctBy(c => c);明显地脱裤子放屁,这原因在于 Distinct<TSource>(IEnumerable<TSource>) 会使用元素的相等比较,所以用字串自己的相等比较就可以决定『独特的字串』(注1),所以在此等情境下,直接用原来的 Distinct 就可以了。
另外一个例子就会凸显出 DistinctBy 是比较容易撰写的,假设我们要独一化的条件是『字串长度』,在这个需求下如果用 Distinct 就得先实作一个 EqualityComparer:
 public class StringLengthEqualityComparer : IEqualityComparer<string>
 {
     public bool Equals(string x, string y)
     {
         if (x == y) { return true; }
         if (x == null || y == null) { return false; }
         return x.Length == y.Length;
     }
     public int GetHashCode(string obj)
     {
         if (obj == null) { return -1; }
         return obj.Length;
     }
 }然后这么使用它:
var distinctColorsByLength = colors.Distinct(new StringLengthEqualityComparer());但如果用 DistinctBy 可以直接写,无须另外新增类别:
distinctColorsByLength = colors.DistinctBy(c => c.Length);注1: 我说的笼统了一些,事实上原始码内部要取得实作的 EqualityComparer 另外牵扯到要不要区分大小写的问题,还挺啰嗦一把的。
Union vs UnionBy
UnionBy 的替代形式近似于 DistinctBy,假设有以下两个阵列,Union (联集) 的条件是字串的长度:
 string[] colors = { "red", "green", "blue", "green", "yellow", "pink" };
 string[] insects = { "ant", "bee", "beetle", "fly",  "butterfly", "grasshopper" };用 Union 会这样写,和前面的 Distinct 一样,必须先建立一个 EqualityComparer:
 public class StringLengthEqualityComparer : IEqualityComparer<string>
 {
     public bool Equals(string x, string y)
     {
         if (x == y) { return true; }
         if (x == null || y == null) { return false; }
         return x.Length == y.Length;
     }
     public int GetHashCode(string obj)
     {
         if (obj == null) { return -1; }
         return obj.Length;
     }
 } var result = colors.Union(insects, new StringLengthEqualityComparer());这个情况使用 UnionBy 就简单多了:
var result = colors.UnionBy(insects, c => c.Length);ExceptBy 与 IntersectBy
这两个会一起谈是有原因的,因为 ExceptBy 和 IntersectBy 的使用情境和 UnionBy 是不一样的。
UnionBy 还是两个同元素的集合取得联集,瞧瞧 UnionBy 的前两个参数都是 IEnumerable<TSource>,但 ExceptBy 和 IntersectBy 第一个参数是 IEnumerable<TSource> ,第二个却是 IEnumerable<TKey>,而原来的 Union/Except/Intersect 方法前两个参数都是 IEnumerable<TSource>。
IEnumerable<TSource> 代表的是来源序列,这没问题;至于第二个 IEnumerable<TKey> 则是作为 Except 或 Intersect 条件的键值,这样说有点模糊,我们来举个例子。
假设有一个 Student 阵列里有七个元素:
 static Student[] CreateStudents()
 {
     new Student { Id = 1, Name = "Bill" },
     new Student { Id = 2, Name = "Steve" },
     new Student { Id = 3, Name = "Ram" },
     new Student { Id = 4, Name = "Alex" },
     new Student { Id = 5, Name = "Mary" },
     new Student { Id = 6, Name = "David" },
     new Student { Id = 7, Name = "Steve" }
 };我们想要以 Id 值做 Except 或 Intersect,所以来一个 int 阵列:
 int[] ids = { 1, 4, 6 };于是我们就可以这样搞:
 static void Main(string[] args)
 {
     int[] ids = { 1, 4, 6 };
     Student[] students = CreateStudents();
     var result = students.ExceptBy(ids, s => s.Id);
     Display(result);
     Console.WriteLine("--------------");
     result = students.IntersectBy(ids, s => s.Id);
     Display(result);
 }範例
本次的範例比较分散:
- DistinctBy Sample
- UnionBy Sample
- ExceptBy and IntersectBy Sample
参考资料
Microsoft Learn .NET6 新的 LINQ API

 用户投稿
用户投稿 微信扫一扫打赏
	    	微信扫一扫打赏
	     支付宝扫一扫打赏
	    	支付宝扫一扫打赏	
	    