導入
IComparer<T>を渡せば「並べ替えのルール」を外から差し込め、IEqualityComparer<T>を渡せば「等しさのルール」を差し込めます。さらに、IEnumerable<string>をIEnumerable<object>として扱える共変性など、ジェネリック型の代入の柔軟さ(変性)も押さえておくと、型エラーの理由が腑に落ちます。
図解
flowchart TB
L["List を並べ替えたい"] --> C["IComparer<T> でルールを渡す"]
C --> R["文字数順・降順など自由に"]
V["共変性(out)"] --> V1["IEnumerable<string> を<br/>IEnumerable<object> として扱える"]
サンプル
var words = new List<string> { "banana", "ok", "apple", "hi" };
// IComparer の代わりにラムダで「文字数順」の並べ替えルールを渡す
words.Sort((a, b) => a.Length.CompareTo(b.Length));
Console.WriteLine(string.Join(",", words)); // ok,hi,apple,banana
// OrderBy にキーを渡すのも「ルールを渡す」考え方(LINQ)
var byLen = words.OrderByDescending(w => w.Length);
Console.WriteLine(byLen.First()); // banana
// 共変性(out): IEnumerable<string> を IEnumerable<object> として扱える
IEnumerable<string> names = new[] { "Taro", "Hanako" };
IEnumerable<object> objects = names; // string は object の一種なのでOK
Console.WriteLine(objects.Count()); // 2
List<T>.SortやOrderByに「比較ルール(ラムダやIComparer<T>)」を渡すと並べ替えを自由に定義できる- 「等しさ」を差し替えたいときは
IEqualityComparer<T>を使う(DistinctやDictionaryのキー比較で活躍) - 共変性(
out):IEnumerable<string>をIEnumerable<object>として扱える(要素を取り出す方向に安全) - 反変性(
in):Action<object>をAction<string>として扱える(受け取る方向に安全)
演習
var items = new List<string> { "ccc", "a", "bb" };
// TODO: items を「文字数の短い順」に並べ替え、カンマ区切りで出力してください("a,bb,ccc")
___
- 期待される出力:
a,bb,ccc
ヒント1を見る
items.Sort((x, y) => x.Length.CompareTo(y.Length)); で並べ替え、string.Join(",", items)で出力
ヒント2を見る
items.Sort((x, y) => x.Length.CompareTo(y.Length)); Console.WriteLine(string.Join(",", items));
まとめ
- 並べ替え・等価判定のルールは
IComparer<T>/IEqualityComparer<T>やラムダで外から渡せる - 共変性(
out)は取り出す方向、反変性(in)は受け取る方向の代入を安全に緩める - 「ルールを外から渡す」発想は次のデリゲート・ラムダに直結する
次回: 処理そのものを変数に入れる「デリゲート」です。