コレクション【C#】

C#文法:コレクション:C06_ListTからArrayクラスのメソッドが呼ばれている

今回は、List<T>の検索系メソッドが、内部ではArrayクラスのメソッドを呼び出している、というお話をします。配列のセクションでIndexOfやLastIndexOf、Findといったメソッドを確認しましたが、これらはList<T>でも同じように使えます。ただし呼び出し方が少し違います。その違いと、内部で何が起きているのかを、実際にList<T>のソースコードを開いて確かめていきます。

確認用のボタンを用意する

いつものように、フォームに確認用のボタンを1つ追加します。ボタンのテキストは「List<T>からArrayクラスのメソッドが呼ばれている」としておき、ダブルクリックしてクリックイベントを生成します。

図1 確認用のボタンを追加してダブルクリックする

図1 確認用のボタンを追加してダブルクリックする

生成されたbutton17_Clickの中に、今回のテーマをコメントで書いておきます。配列のときに紹介したメソッドはArrayクラスのstaticメソッドでしたが、List<T>ではインスタンスメソッドとして利用できる、というのが今回のポイントです。対象になるのはIndexOf、LastIndexOf、Find、FindLast、FindAllといったメソッドです。

図2 今回のテーマをコメントで整理する

図2 今回のテーマをコメントで整理する

リスト1 button17_Clickに書いたコメント

private void button17_Click(object sender, EventArgs e)
{
    //List<T>からArrayクラスのメソッドが呼ばれている
    //Arrayで紹介したメソッドは
    //インスタンスメソッドとして利用できる
    //(配列はstaticメソッド)
    //IndexOf、LastIndexOf、Find、FindLast、FindAll
}

List<T>でIndexOfを呼び出す

まず、検索対象となるList<string>を用意します。new List<string>()で生成して、Addメソッドで「AAA」「BBB」「CCC」の3つを追加します。

図3 List<string>を生成して3つの要素を追加する

図3 List<string>を生成して3つの要素を追加する

このリストから「BBB」の位置を調べたい場合は、list.と入力して出てくるIndexOfを使います。引数には探したい値だけを渡します。戻り値は見つかった位置のインデックスなので、indexという変数で受け取っておきます。

図4 list.IndexOf("BBB")と記述する

図4 list.IndexOf(“BBB”)と記述する

リスト2 List<T>のIndexOf

var list = new List<string>();
list.Add("AAA");
list.Add("BBB");
list.Add("CCC");

var index = list.IndexOf("BBB");

配列のときの書き方と比べる

配列のときはどうだったかを思い出してみましょう。配列の場合はArrayクラスのIndexOfを使うので、1つ目の引数に検索対象の配列そのものを渡し、2つ目の引数に探したい値を指定していました。比較のために、すぐ下に配列版も書いておきます。

図5 比較用に配列版のArray.IndexOfも書いておく

図5 比較用に配列版のArray.IndexOfも書いておく

リスト3 配列のIndexOf(比較用)

string[] hai = new string[3];
Array.IndexOf(hai, "BBB");

2つを並べると違いがはっきりします。配列はArray.IndexOf(hai, "BBB")のように、クラス名から呼び出すstaticメソッドで、対象の配列を引数として渡す必要があります。一方、List<T>はlist.IndexOf("BBB")のように、インスタンスメソッドです。自分自身を引数に渡さなくても、自分の中にあるIndexOfを呼び出せるわけです。

リスト4 staticメソッドとインスタンスメソッドの違い

// 配列:Arrayクラスのstaticメソッド(1つ目の引数に配列を渡す)
Array.IndexOf(hai, "BBB");

// List<T>:インスタンスメソッド(自分自身を渡す必要はない)
list.IndexOf("BBB");

F12でList<T>の中身を見る

では、List<T>のIndexOfは内部的にどうなっているのでしょうか。コード上のListにカーソルを置いてF12キーを押すと、List<T>クラスのソースコードを見ることができます。

図6 Listにカーソルを置いてF12キーを押す

図6 Listにカーソルを置いてF12キーを押す

List.csが開きます。クラスの冒頭にあるコメントを読むと、List<T>は要素を格納するために配列を使う、サイズ可変のリストであると説明されています。つまりList<T>は、内部に配列を持っているクラスです。

図7 List.csが開く。冒頭のコメントに「配列を使って要素を格納する」とある

図7 List.csが開く。冒頭のコメントに「配列を使って要素を格納する」とある

このファイルの中を「IndexOf」で検索してみます。するとIndexOfメソッドの定義が見つかります。

図8 List<T>のIndexOfの中でArray.IndexOfが呼ばれている

図8 List<T>のIndexOfの中でArray.IndexOfが呼ばれている

リスト5 List<T>のIndexOf(List.csより)

// This method uses the Array.IndexOf method to perform the
// search.
//
public int IndexOf(T item)
    => Array.IndexOf(_items, item, 0, _size);

中身はたった1行で、Array.IndexOfを呼び出しているだけです。1つ目の引数の_itemsが、List<T>が内部に持っている配列です。3つ目と4つ目の引数は検索の開始位置と件数で、先頭の0から、実際に入っている要素数である_sizeの分だけを検索する、という指定になっています。

つまり、List<T>は内部的に配列を持っているので、配列のメソッドがそのまま使えます。ただし、私たちがArray.IndexOfをstaticで呼び出すのではなく、List<T>自身の中に用意されたメソッドが代わりに呼び出してくれています。ですから使う側としては、list.と入力して出てくるIndexOfやFindといったメソッドを使えばOK、ということになります。

実行して確認する

最後に一度動かしておきましょう。IndexOfの行にブレークポイントを置いて実行し、追加したボタンをクリックします。ステップ実行で1行進めてindexにマウスカーソルを当てると、値は1になっています。「BBB」は2番目の要素なので、インデックス1が正しく取得できています。

図9 indexに1が入っていることを確認する

図9 indexに1が入っていることを確認する

まとめ

ここまでの内容を、コードにコメントとして残しておきます。

図10 確認した内容をコメントとして残す

図10 確認した内容をコメントとして残す

リスト6 今回の完成コード

private void button17_Click(object sender, EventArgs e)
{
    //List<T>からArrayクラスのメソッドが呼ばれている
    //Arrayで紹介したメソッドは
    //インスタンスメソッドとして利用できる
    //(配列はstaticメソッド)
    //IndexOf、LastIndexOf、Find、FindLast、FindAll

    var list = new List<string>();
    list.Add("AAA");
    list.Add("BBB");
    list.Add("CCC");

    //配列のstaticメソッドとは異なり、
    //インスタンスメソッドで提供
    var index = list.IndexOf("BBB");
    //List<T>のIndexOfを呼び出すと、
    //Array.IndexOf(_items, item, 0, _size)が
    //呼び出されている

    string[] hai = new string[3];
    Array.IndexOf(hai, "BBB");
}

配列のときはstaticメソッドでしたが、List<T>ではインスタンスメソッドとして提供されています。そして実際には、内部でArrayクラスのIndexOfが呼び出されています。List<T>は配列を内部に持ち、配列の機能を使いやすい形で包んでくれているクラスだ、ということが、ソースコードからも確認できました。

C#文法:コレクション

■非公開コース「C#14新機能」プレゼント:
非公開コース「C#14新機能」(80分)をご覧になりたい方は
こちらからURLとパスワードを発行していますので、ご覧になってみてください。
非公開コース「C#14新機能」を観る

A01_はじめに
A02_プロジェクトの作成
B01_配列とは
B02_配列の生成とアクセス
B03_生成と同時に値を設定する
B04_型推論による生成
B05_メソッドの引数などにする場合の注意点
B06_Length
B07_IndexOfでの検索
B08_FindIndexでの検索
B09_Find
B10_Exists
B11_FindAllとFindLast
B12_誤ったコピー
B13_Array.Copy
B14_範囲指定のコピー
B15_Resize
C01_ArrayList
C02_List
C03_List 動的な要素の変更
C04_Listのコンストラクタ
C05_Listのコンストラクタ_Capacity
C06_ListTからArrayクラスのメソッドが呼ばれている
D01_コレクションインタフェースとは
D02_異なるコレクションクラスに互換性を持たせる
D03_インタフェースの階層構造
D04_クライアントができる操作を制限させる
D05_クライアントができる操作を制限させる_後半
D06_Enumerableの拡張メソッドに関して
D07_ReadOnly系のコレクションインタフェース
D08_AsReadOnly
D09_ToListでコピーする
D10_ListTはprivateで使う

■非公開コース「C#14新機能」プレゼント:
非公開コース「C#14新機能」(80分)をご覧になりたい方は
こちらからURLとパスワードを発行していますので、ご覧になってみてください。
非公開コース「C#14新機能」を観る