本文へスキップ
BecomeCoder

C#オブジェクト指向コース · 第4章 ジェネリクスとデリゲート · レッスン28

共変性・反変性 と 比較子

ブラウザで完結

導入

IComparer<T>を渡せば「並べ替えのルール」を外から差し込め、IEqualityComparer<T>を渡せば「等しさのルール」を差し込めます。さらに、IEnumerable<string>IEnumerable<object>として扱える共変性など、ジェネリック型の代入の柔軟さ(変性)も押さえておくと、型エラーの理由が腑に落ちます。

図解

flowchart TB
    L["List を並べ替えたい"] --> C["IComparer&lt;T&gt; でルールを渡す"]
    C --> R["文字数順・降順など自由に"]
    V["共変性(out)"] --> V1["IEnumerable&lt;string&gt; を<br/>IEnumerable&lt;object&gt; として扱える"]

サンプル

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>.SortOrderByに「比較ルール(ラムダやIComparer<T>)」を渡すと並べ替えを自由に定義できる
  • 「等しさ」を差し替えたいときはIEqualityComparer<T>を使う(DistinctやDictionaryのキー比較で活躍)
  • 共変性(outIEnumerable<string>IEnumerable<object>として扱える(要素を取り出す方向に安全)
  • 反変性(inAction<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)は受け取る方向の代入を安全に緩める
  • 「ルールを外から渡す」発想は次のデリゲート・ラムダに直結する

次回: 処理そのものを変数に入れる「デリゲート」です。

実際に動かしてみよう

本文のサンプルや演習のコードは、コードブロック右上の「コピー」ボタンでコピーして、下のエディタに貼り付ければそのまま実行できます。

C# — ブラウザ内で実行

ブラウザ内でC#を動かす環境を読み込みます(初回のみ数秒)。
スクロールして表示された時点でも自動で読み込まれます。