こんにちは、働くC#プログラマーのさんさめです。
LINQの中でも解説が最も不要であろうメソッドの1つが、
MaxとMinでしょう。
その名の通り、リストの中の
最大値、最小値を返すメソッドです。
ただの解説記事の場合ここで終わっても
おかしくないです。
ですが、Max,Minは
使う際に気を付けなければいけない
やや直感的に使いにくい点が2つあります。
- 要素が無いリストに使うと例外になる
- クラスに使うと数値そのものしか返ってこない
本記事では、
上記の2点について解説します。
要素が無いリストに使うと例外になる
いきなりどでかい罠です。
要素が1つも無いリストに対して
このメソッドを呼び出すと例外
(InvalidOperationException)になります。
この罠挙動、
どこかで見たことがありますね。
そう、Firstです。
Firstについては以下の記事をご覧ください。
【LINQ】Firstは使ってはいけない!FirstOrDefaultを使おう
つまり、必ず事前に要素が存在するかの
チェックを行わなければなりません。
var list = new[]
{
2,
4,
6,
8,
10
};
var oddList = list.Where(x => x % 2 == 1);
if (oddList.Any())
{
Console.WriteLine($"奇数の中の最大値は{oddList.Max()}です");
}
else
{
Console.WriteLine("奇数は無かった");
}
これが面倒なのよ…
とはいえ、じゃあ無かった時は何を返せば良いのか、
という話にはなるので例外にするしかなかったのかもしれません。
ただ、使う側からすると
いきなりアプリが落ちるのは迷惑な話です。
番兵のように、
その型の最小値(Minの場合は最大値)を返すメソッドだけでも
あったらありがたかったと思うんですけどね。
// 例えばこういうの。全型分用意するのが大変なのでわざわざ作らないけれども
public static int MaxOrGuard(this IEnumerable<int> source, int guard = int.MinValue)
=> source.Any() ? source.Max() : guard;
クラスに使うと数値そのものしか返ってこない
Max、Minは、特定の条件を満たさない限り、
自作クラスや整数型以外にはそのままは使えません。
ですが、Max,Minには変換関数をかますことのできる
便利なオーバーロードがあります。
var list = new[]
{
new Card("Spade", 1),
new Card("Clover", 2),
new Card("Diamond", 3),
new Card("Heart", 4),
};
// 変換関数を噛ますとクラスの情報は失われてしまう
Console.WriteLine(list.Max(x => x.Number)); // 4
このオーバーロード、一見便利なのですが、
あくまでも変換した後にその最大値を返すだけなので、
クラスそのものを返すメソッドが存在しません。
あくまで、list.Select(x => x.Number).Max()
の簡略形でしかないということです。
つまり、上記の例でいうと、
「最大の数字を持っているカード」
を取得するLINQメソッドは存在しないのです。
そういうことをしたい場合は、
比較したいクラスがIComparable<T>を
実装している必要があります。
// これがあれば…
public class Card : IComparable<Card>
{
public int CompareTo(Card other) => Number.CompareTo(other.Number);
public int Number { get; set; }
public string Mark { get; set; }
public override string ToString() => $"{Mark} : {Number}";
}
var list = new[]
{
new Card("Spade", 1),
new Card("Clover", 2),
new Card("Diamond", 3),
new Card("Heart", 4),
};
// こうしてOK
Console.WriteLine(list.Max()); // Heart : 4
普通に面倒だし、そもそも
手出しできないクラスの場合はお手上げ
ただし、.Net 6 以降であれば、
MaxByというまさに求める挙動をするメソッドが
追加されています。
.Net Frameworkには残念ながら存在しないので、
本家のコードを参考に実装すると良いでしょう。
このように、クラスに使おうと思うと
用途によってはひと手間いることに注意が必要です。
まとめ
まとめです。
- Max,Minはリストの最大値、最小値を求める
- 要素の無いリストに使うと例外になる
- クラスに使おうとするとひと手間いる
最後までお読みいただきありがとうございました。
コメント