Linq 新功能 (1) MaxBy與MinBy

趁著最近空閒,來整理一下從 .NET 6 之後 Linq 上的新功能。

本集提要
  • 框架 : .NET 6
  • 功能 : MaxBy, MinBy
說明

Linq 的 Max與Min 方法可以協助我們取得一串執行個體中某個屬性的最大值或最小值,當我們想要取得的是具有最大值或最小值的執行個體就得要拐彎。

例如有以下序列:

 var people = new List<Person>
 {
     new Person { Name = "Alice", Age = 30 },
     new Person { Name = "Bob", Age = 25 },
     new Person { Name = "Charlie", Age = 35 }
 };

如果要取得最大的年齡,通常我們會這麼寫:

int maxAge = people.Max(p => p.Age);

但需求如果是要取得具有最大年齡的那個執行個體,可能會有一些其他做法,例如 (先不考慮強固性的問題):

(1) 先 OrderByDescending 再 First

Person oldestPerson1 = people.OrderByDescending(p => p.Age).First();

(2) 先取 Max 再 First

int maxAge = people.Max(p => p.Age);
Person oldestPerson2 = people.First(p => p.Age == maxAge);

老實說也沒真的很難啦。不過微軟在 .NET 6 提供了更方便的方法,MaxBy 與 MinBy:

 Person oldestPerson3 = people.MaxBy(p => p.Age);
 Person youngestPerson3 = people.MinBy(p => p.Age);

上述的範例在此

Benchmark

我也好奇測試了一下效能,MaxBy 和 MinBy 似乎比舊式的方式要快一些,而且使用更少的記憶體,可以在這邊看到 Benchmark 測試的程式碼

兩次記錄的結果如下:

// * Summary *

BenchmarkDotNet v0.14.0, Windows 11 (10.0.22631.4751/23H2/2023Update/SunValley3)
12th Gen Intel Core i7-1265U, 1 CPU, 12 logical and 10 physical cores
.NET SDK 9.0.200-preview.0.24575.35
  [Host]     : .NET 9.0.0 (9.0.24.52809), X64 RyuJIT AVX2
  DefaultJob : .NET 9.0.0 (9.0.24.52809), X64 RyuJIT AVX2


| Method                   | Mean     | Error     | StdDev    | Gen0   | Allocated |
|------------------------- |---------:|----------:|----------:|-------:|----------:|
| CallOrderByDescThenFirst | 4.348 us | 0.0579 us | 0.0542 us | 0.0153 |     128 B |
| CallMaxThenFirst         | 4.446 us | 0.0699 us | 0.0653 us | 0.1144 |     760 B |
| CallMaxBy                | 3.727 us | 0.0327 us | 0.0306 us | 0.0038 |      40 B |
| CallOrderByThenFirst     | 4.913 us | 0.0583 us | 0.0545 us | 0.0153 |     128 B |
| CallMinThenFirst         | 3.870 us | 0.0435 us | 0.0407 us | 0.0381 |     280 B |
| CallMinBy                | 3.654 us | 0.0565 us | 0.0501 us | 0.0038 |      40 B |


// * Summary *

BenchmarkDotNet v0.14.0, Windows 11 (10.0.22631.4751/23H2/2023Update/SunValley3)
12th Gen Intel Core i7-1265U, 1 CPU, 12 logical and 10 physical cores
.NET SDK 9.0.200-preview.0.24575.35
  [Host]     : .NET 9.0.0 (9.0.24.52809), X64 RyuJIT AVX2
  DefaultJob : .NET 9.0.0 (9.0.24.52809), X64 RyuJIT AVX2


| Method                   | Mean     | Error     | StdDev    | Gen0   | Allocated |
|------------------------- |---------:|----------:|----------:|-------:|----------:|
| CallOrderByDescThenFirst | 4.406 us | 0.0863 us | 0.0848 us | 0.0153 |     128 B |
| CallMaxThenFirst         | 4.511 us | 0.0591 us | 0.0553 us | 0.1068 |     712 B |
| CallMaxBy                | 3.659 us | 0.0350 us | 0.0292 us | 0.0038 |      40 B |
| CallOrderByThenFirst     | 4.730 us | 0.0684 us | 0.0640 us | 0.0153 |     128 B |
| CallMinThenFirst         | 3.944 us | 0.0670 us | 0.0627 us | 0.0763 |     520 B |
| CallMinBy                | 3.633 us | 0.0588 us | 0.0550 us | 0.0038 |      40 B |