インデクサ

プログラミング言語におけるインデクサ （英: indexer） は、クラスや構造体のインスタンスに配列と同様の添字を指定してアクセスするための構文である。 言語仕様に配列を持つプログラミング言語の多くは、配列要素にアクセスするための添字による特殊な構文を持つ。例えばC言語から派生した言語では、添え字演算子[]により要素アクセスできる。以下はC#の例である。 double[] array = new double[10]; for (int i = 0; i < array.Length; ++i) { array[i] = i * 0.1; } System.Console.WriteLine(array[5]); その一方で、例えば動的配列 (配列リスト) や辞書 (連想配列) などのように、（言語組み込みの）配列以外のデータ構造を持つコレクションオブジェクトの要素に対しても、配列のように添え字でアクセスできると便利かつ直感的である。 インデクサがない言語では通常getterやsetterと呼ばれる要素アクセスのためのメソッドを実装したり、内部の配列をプロパティとして公開するなどしてアクセスするのが一般的であるが、以下の問題点がある。 コードの冗長化による可読性や直感性の低下 言語によってはgetやsetが識別子として扱われている場合があり、アクセサメソッドの名前が冗長化しやすい 内部配列（実装）を外部に公開するようなパターンは（カプセル化の観点から）そもそも避けるべきである インデクサを用いることでオブジェクト内のコレクション要素へのアクセスを、配列へのアクセスと同様に記述できる。 ただし、インデクサは実質的にはgetter/setterメソッド呼び出しの糖衣構文であり、インデクサそのものは当然ながら反復子としての機能は持たない。インデクサを記述したからといって配列のようにforeach文などの反復構文がそのまま使えるわけではない他、メソッド呼び出しのオーバーヘッド減少などの効果も特にない（コンパイラによるインライン展開等は当然受けるが、インデクサ固有の利点ではない）。 プログラミング言語C#のインデクサはC++の添え字演算子の多重定義と似ているが、次のような点で発展したものだと見ることもできる。ただしC++とC#の言語設計上の事情も絡んでいるので、単純に比較できるものではない。 インデクサではプロパティのように値を得るときと代入するときとで実際には別のメソッドに分かれている。 インデクサは単独で多次元な配列を模倣できる。C++で同じようなことをするには間に一時的にオブジェクトを入れるなどの技巧を凝らす必要がある[要説明]。 他の言語ではC#のインデクサに似た機能として、Visual Basic .NETの引数付きプロパティやC++/CLIのインデックス付きプロパティなどが存在する。 インデクサをサポートしない言語、例えばJavaにおいて、配列リストを表すコレクションの要素へのアクセスは、次のようなjava.util.Listインターフェイスのget/setメソッドによって提供される。 var list = new java.util.ArrayList<Integer>(java.util.Collections.nCopies(10, 0)); // index 番目の要素に値を設定。 // void set(int index, E element) list.set(2, 100); // index 番目の要素を取得。 // E get(int index) int val = list.get(2); 一方、C#のインデクサでは、配列リストの要素へのアクセスを配列のアクセスと同じように記述することができる。以下の例ではSystem.Collections.Generic.IListインターフェイスで定義されているインデクサを使用している。 var list = new System.Collections.Generic.List<int>(new int[10]); list[2] = 100; int val = list[2]; インデクサを定義する際、インデックスとして整数以外の値 (文字列やオブジェクトなど) も使用することができ、ハッシュテーブルなどの連想配列を表すコレクションに使用されている。 var map = new System.Collections.Generic.Dictionary<string, double>(); map["key1"] = 0.1; double val = map["key1"]; なお、連結リスト実装であるSystem.Collections.Generic.LinkedListは、要素アクセスの計算量が O ( 1 ) {\displaystyle O(1)} ではなく O ( n ) {\displaystyle O(n)} であり、インデクサは提供されない。 C#においては、クラスおよび構造体の内部にインデクサを持つことができる。 インデクサはthis[添え字リスト]の形式で宣言する。 インデクサの構文内ではget/setが、setアクセサ内ではvalueがそれぞれ文脈キーワードとして機能する。 class Matrix2x2 { private readonly int[][] items = new[]{ new[] { 0, 0 }, new[] { 0, 0 }, }; // セルの値を取得、設定するインデクサ。 public int this[int index0, int index1] { get { return items[index0][index1]; } set { items[index0][index1] = value; } } } 呼び出し側では、配列にアクセスするような構文でインデクサの機能を呼び出す。 var matrix = new Matrix2x2(); // setアクセス matrix[0, 0] = 10; // getアクセス int num = matrix[0, 1]; // get ⇒ 加算 ⇒ set matrix[1, 0] += 10; 通常のメソッドと異なり、代入を伴わないインデクサメソッドの呼び出しは行えない。下記のような呼び出しは構文エラーとなる他、戻り値void型のインデクサを定義することもできない。ただし、インデクサを介して呼び出したオブジェクトのメンバを呼び出すような場合は構文上問題ない。 // 構文エラーとして扱われる。 matrix[1, 0]; // 次の呼び出しは問題ない（この例では意味をなさない呼び出しだが） matrix[1, 0].ToString(); 他言語との相互運用のため、既定ではItemという名前のインデックス付きプロパティが自動生成される。 System.Runtime.CompilerServices.IndexerNameAttribute属性を付加することで、自動生成されるインデクサの名前を明示的に指定できる。 この機能が利用されている代表的な例として、System.Stringクラスが挙げられる。System.Stringクラスではインデクサの名前はCharsとなるように指定されている。 Visual Studio IDE や Visual Studio Code では、以下のようなコードスニペットが用意されている。 indexer : get,setアクセサを持つ標準的なインデクサ Visual Basic においては、Defaultキーワード付きのインデックス付きプロパティがインデクサとしてアクセス可能となる。 Public Class StringArray Private m_Item As String() = New String(10) {} ' Defaultが宣言されたインデックス付きプロパティはインデクサとしてアクセス可能 Default Public Property Item(index As Integer) As String Get Return m_Item(index) End Get Set(ByVal value As String) m_Item(index) = value End Set End Property End Class Dim obj As StringArray = New StringArray ' getアクセス Dim str As String = obj(1) ' setアクセス obj(2) = str