インデクサの使用 (C# プログラミング ガイド)
更新 : 2007 年 11 月
インデクサは、構文を簡略化します。これを使用すると、クラス、構造体、または インターフェイスを作成できます。クライアント アプリケーションは配列と同じようにアクセスできます。インデクサは、内部コレクションまたは内部配列をカプセル化することが主な目的である型で最も多く実装されます。たとえば、TempRecord という名前のクラスがあるとします。これは温度を華氏で表し、24 時間のうちに 10 回、異なる時刻に温度を記録します。このクラスには float 型の "temps" という名前の配列が含まれており、これは温度を表します。また、DateTime も含まれており、これは温度が記録された日付を表します。このクラスにインデクサを実装すると、クライアントは TempRecord インスタンスの温度に、float temp = tr.temps[4] ではなく float temp = tr[4] としてアクセスすることができます。インデクサはクライアント アプリケーションの構文を簡略化するだけでなく、クラスとその目的を、他の開発者たちにとって分かりやすい、より直感的なものとします。
クラスまたは構造体でインデクサを宣言するには、次の例のように、this キーワードを使用します。
public int this[int index] // Indexer declaration
{
// get and set accessors
}
解説
インデクサの型とパラメータの型は、少なくともインデクサ自体と同程度のアクセシビリティが必要です。アクセシビリティのレベルの詳細については、「アクセス修飾子」を参照してください。
インターフェイスでインデクサを使用する方法の詳細については、「インターフェイスのインデクサ (C# プログラミング ガイド)」を参照してください。
インデクサのシグネチャは、番号と仮パラメータの型で構成されています。インデクサの型または仮パラメータの名前は含まれません。同じクラスで複数のインデクサを宣言する場合は、異なるシグネチャを指定する必要があります。
インデクサの値は変数には分類されないので、インデクサの値を ref パラメータまたは out パラメータとして渡すことはできません。
インデクサに他の言語で使用できる名前を指定するには、宣言に name 属性を使用します。たとえば、次のようにします。
[System.Runtime.CompilerServices.IndexerName("TheItem")]
public int this [int index] // Indexer declaration
{
}
このインデクサの名前は TheItem になります。name 属性を指定しないと、Item が既定の名前になります。
例 1
説明
次の例は、プライベートな配列フィールド temps とインデクサの宣言方法を示しています。インデクサを使うと、インスタンス tempRecord[i] に直接アクセスできます。インデクサを使わない場合は、配列を public メンバとして宣言し、そのメンバ tempRecord.temps[i] に直接アクセスします。
Console.Write ステートメントなどでインデクサのアクセスが評価されると、get アクセサが呼び出されることに注意してください。したがって、get アクセサがない場合は、コンパイル エラーが発生します。
コード
class TempRecord
{
// Array of temperature values
private float[] temps = new float[10] { 56.2F, 56.7F, 56.5F, 56.9F, 58.8F,
61.3F, 65.9F, 62.1F, 59.2F, 57.5F };
// To enable client code to validate input
// when accessing your indexer.
public int Length
{
get { return temps.Length; }
}
// Indexer declaration.
// If index is out of range, the temps array will throw the exception.
public float this[int index]
{
get
{
return temps[index];
}
set
{
temps[index] = value;
}
}
}
class MainClass
{
static void Main()
{
TempRecord tempRecord = new TempRecord();
// Use the indexer's set accessor
tempRecord[3] = 58.3F;
tempRecord[5] = 60.1F;
// Use the indexer's get accessor
for (int i = 0; i < 10; i++)
{
System.Console.WriteLine("Element #{0} = {1}", i, tempRecord[i]);
}
// Keep the console window open in debug mode.
System.Console.WriteLine("Press any key to exit.");
System.Console.ReadKey();
}
}
他の値を使用したインデックス作成
C# では、インデックスの型は整数に限定されません。たとえば、文字列をインデクサに使用することが有効なこともあります。このようなインデクサは、コレクション内の文字列を検索し、適切な値を返す場合に実装されることがあります。アクセサをオーバーロードできるため、文字列と整数のバージョンは共存できます。
例 2
説明
この例では、曜日を格納するクラスが宣言されています。get アクセサは、曜日の名前を示す文字列を取得すると、対応する整数を返すように宣言されています。たとえば、Sunday の場合は 0、Monday の場合は 1 などの値を返します。
コード
// Using a string as an indexer value
class DayCollection
{
string[] days = { "Sun", "Mon", "Tues", "Wed", "Thurs", "Fri", "Sat" };
// This method finds the day or returns -1
private int GetDay(string testDay)
{
for(int j = 0; j < days.Length - 1; j++)
{
if (days[j] == testDay)
{
return j;
}
}
throw new System.ArgumentOutOfRangeException(testDay, "testDay must be in the form \"Sun\", \"Mon\", etc");
}
// The get accessor returns an integer for a given string
public int this[string day]
{
get
{
return (GetDay(day));
}
}
}
class Program
{
static void Main(string[] args)
{
DayCollection week = new DayCollection();
System.Console.WriteLine(week["Fri"]);
// Raises ArgumentOutOfRangeException
System.Console.WriteLine(week["Made-up Day"]);
// Keep the console window open in debug mode.
System.Console.WriteLine("Press any key to exit.");
System.Console.ReadKey();
}
}
// Output: 5
プログラミングの注意点
インデクサのセキュリティと信頼性を改善するには、主に 2 つの方法があります。
クライアント コードが無効なインデックス値を渡してきても、それを処理できるように必ずエラー処理を組み込んでください。このトピックの最初の例の TempRecord クラスには Length プロパティが用意されており、入力がインデクサに渡される前にクライアント コードでの検証が行われるようになっています。エラー処理コードをインデクサ自体の内部に置くこともできます。インデクサのアクセサ内部でスローされる例外はすべて、ユーザーのためにドキュメント化してください。詳細については、「例外のデザインのガイドライン」を参照してください。
get アクセサと set アクセサのアクセシビリティを設定し、適切な制限を指定します。これは、set アクセサの場合、特に重要です。詳細については、「非対称アクセサのアクセシビリティ (C# プログラミング ガイド)」を参照してください。