LoginSignup
10
2

More than 1 year has passed since last update.

Ix.NETのMaxBy、.NET 6から加わった標準メソッドと名称が衝突したので、MaxByWithTiesになってた

Last updated at Posted at 2022-12-24

概要

「Rx.NETやUniRxにあるXXXっていうメソッド(オペレーター)、なんでLINQにはないの?」って思った時におすすめなのがIx.NET。

以下、READMEより

The Interactive Extensions (Ix) is a .NET library which extends LINQ to Objects to provide many of the operators available in Rx but targeted for IEnumerable.

さてそんなIx.NETには以前から、「要素の中で一番大きい評価値をもつ要素を探すこと」ができるMaxByというメソッドが存在しました。このような機能を提供するメソッドは、LINQが登場してから長い間、標準ライブラリのLINQとしてはありませんでした。

ところが.NET 6で、標準ライブラリとしてLINQに「MaxBy」という同名で、Ix.NET版とは微妙に仕様が違うメソッドが追加されました。

それを受けてIx.NETではv6.0.1で、MaxByというメソッドを「MaxByWithTies」という名前に変更しました。同様に、MinByというメソッドを「MinByWithTies」という名前に変更しました。(途中紆余曲折あり)

該当のリリース(v6.0.1)はこちら。

該当の変更PRはこちら。

Ix.NET 5.1.0までのMaxBy

Ix.NET v5.1.0までのMaxByメソッドのシグニチャは以下の通りです。

public static IList<TSource> MaxBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)

返り値型がIList<TSource>なことに注目してください。
Ix.NET v5.1.0までのMaxByメソッドは、「最大と評価された要素」全てを含むリストを返しました。

次のレコードを使って例を示します。

record Player(String Name, int Level);

次のコードでは、MaxByを使ってリストplayersから最大Levelの要素を探します。次のコードを実行すると、maxLevelPlayersは複数の要素を含むことがポイントです。

var players = new List<Player>
{
    new("taro", 5),
    new("jiro", 5),
    new("saburo", 4),
};

// IList<Player>型
var maxLevelPlayers = players.MaxBy(it => it.Level);

// 次のように表示
// Player { Name = taro, Level = 5 }
// Player { Name = jiro, Level = 5 }
Console.WriteLine(string.Join("\n", maxLevelPlayers));

一方で次のコードでは、maxLevelPlayersは単一の要素のみを含みます。

var players = new List<Player>
{
    new("taro", 100),
    new("jiro", 5),
    new("saburo", 4),
};

// IList<Player>型
var maxLevelPlayers = players.MaxBy(it => it.Level);

// 次のように表示
// Player { Name = taro, Level = 5 }
Console.WriteLine(string.Join("\n", maxLevelPlayers));

なお、IEnumerable<TSource>が空の場合、例外を投げます。

このように、Ix.NET v5.1.0までのMaxByメソッドは、「最大と評価された要素」全てを含むリストを返しました。Ix.NETのMaxByでは、「最大と評価された要素」が複数あった場合、返り値のリストに全て含まれるため、全て活用することができました。

.NET 6で加わったMaxByメソッド

.NET 6から、MaxByメソッドが標準ライブラリに加わりました。

メソッドのシグニチャは次の通りです。

public static TSource? MaxBy<TSource,TKey> (this IEnumerable<TSource> source, Func<TSource,TKey> keySelector);

公式ドキュメントはこちら。

返り値の型がIList<TSource>ではなくTSource?なことに注目して下さい。メソッドのシグニチャからわかる通り、要素を一つ返すだけです。仮に「最大と評価された要素」が複数あったとしても、返されるのは一つの要素のみです。なお、返される一つの要素は、「最大と評価された要素」の先頭要素です。(.NET 6の実装では)

次のコードでは、Levelが最大の要素を探しています。最も大きいLevelの要素が2つあることに注目してください。このコードを実行すると、maxLevelPlayerには最初の要素「Player { Name = taro, Level = 5 }」が代入されます。

var players = new List<Player>
{
    new("taro", 5),
    new("jiro", 5),
    new("saburo", 4),
};

// Player型
var maxLevelPlayer = players.MaxBy(it => it.Level);

// 次のように表示
// Player { Name = taro, Level = 5 }
Console.WriteLine(maxLevelPlayer);

このように、.NET 6から加わったMaxByメソッドでは、仮に「最大と評価された要素」が複数あったとしても、返されるのは一つの要素のみのため、返されなかった要素を活用することはできません。

Ix.NET 5.1.0、.NET 6以降でIx.NET版のMaxByなどを除外

MaxByというメソッド(System.Linq名前空間のIEnumerable<TSource>の拡張メソッド)が、次の二つで衝突してしまいました。

  • 元々あったIx.NETのMaxByメソッド(IList<TSource>を返す)
  • .NET 6で標準ライブラリに加わったMaxByメソッド(TSource?を返す)

そのため、Ix.NET 5.1.0では

「.NET 6以降では、MaxByなどいくつかのメソッド除外する」

という変更が行われました。

問題提起のIssueはこちら

Ix.NET 5.1.0のリリースはこちら

該当のPRはこちら

Ix.NET 6.0.1、IListを返すMaxByがMaxByWithTiesとして復活

Ix.NET 6.0.1において、IList<T>を返すMaxByメソッドは名前をMaxByWithTiesと変え、復活しました。

復活の提案のIssueコメントはこちら

該当のPRはこちら

Ix.NET 6.0.1のリリースはこちら

合わせて、Ix.NET 6.0.1では、MaxByやMinByがObsoleteになっています。これは、.NET 6未満でIx.NET版のMaxByを利用した際、次のような警告を出して、新しい「MaxByWithTies」への移行を促すためでしょう。

CS0618: Method 'System.Linq.EnumerableEx.MaxBy(this IEnumerable, Func)' is obsolete: Use MaxByWithTies to maintain same behavior with .NET 6 and later

個人の感想・意見

  • ちょいちょい標準ライブラリにLINQのメソッドが追加されている。標準ライブラリのメソッドと、3rd partyライブラリの名前が衝突して、微妙に仕様が違うこと、今後もありそう。
  • 個人的には、標準ライブラリのTSource?を返すMaxByよりも、Ix.NETのIList<TSource>を返す方が好み
  • Ix.NETのMaxByWithTiesという復活&名称変更はナイス対応
  • 標準ライブラリのLINQに、MaxByは入っていてほしかったな(わがまま)
  • 標準ライブラリのLINQ、MaxByはIList<TSource>返してほしかったな(これはプレビュー段階でフィードバックしなかったのが悪い)
10
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
2