先日、あるクラスのソートのためにICompareble<T>を実装したラッパークラスを作成したのですが、
コードレビューの際にLinqのOrderByを使用すれば良いとの指摘を受けました。
以下のようなクラスとそのリスト、値の演算メソッドがあった際に、
class Hoge {
public int No { get; set; }
public int Value { get; set; }
}
var hogeList = new List<Hoge>(){ /* 要素たくさん */ };
int CalcHeavyForSort(int value) {
var ret = 0;
// 重い演算
return ret;
}
次のように書くことで、
hogeList = hogeList.OrderBy(x => CalcHeavy(x.Value)).ThenBy(x => x.No).ToList();
1. Valueの演算結果により昇順でソート
2. 1によるソートの順序内でNoにより昇順でソート
を実行してソートされたリストを取得できます。
降順ならOrderByDescending, ThenByByDescendingです。
特殊な比較を行いたい場合は、IComparerを引数に与えることもできるようです。
引数で与えたラムダ式の実行も1要素1回だけのようですので安心ですね。
(内部でラムダ式の戻り値のリストを作成し、
インデックス同士を比較するメソッド内でその戻り値リストへアクセスしているようです。)
雑ですが、もしも次のようにソート時に同じ重い演算を複数回実行する必要がある場合は、
hogeList = hogeList.GroupBy(x => CalcHeavy(x.Value))
.ThenBy(x => CalcHeavy(x.Value) * x.No).ToList();
次のように匿名クラスでラッパーを作り、それをソートすると良さそうに思います。
hogeList = hogeList.Select(x => new {
Source = x,
CalcedValue = CalcHeavy(x.Value),
})
.OrderBy(x => x.CalcedValue)
.ThenBy(x => x.CalcedValue % x.Source.No)
.ToList(x => x.Source);
Linqは便利で可読性が高く保守もしやすいので、
シビアなパフォーマンスが求められるケースでなければ積極的に使っていきたいです。