インデクサーの使用 (C# プログラミング ガイド)

インデクサーは、構文を簡略化します。これを使用すると、クラス構造体、または インターフェイスを作成できます。クライアント アプリケーションは配列と同じようにアクセスできます。 インデクサーは、内部コレクションまたは内部配列をカプセル化することが主な目的である型で最も多く実装されます。 たとえば、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();

    }
}
/* Output:
        Element #0 = 56.2
        Element #1 = 56.7
        Element #2 = 56.5
        Element #3 = 58.3
        Element #4 = 58.8
        Element #5 = 60.1
        Element #6 = 65.9
        Element #7 = 62.1
        Element #8 = 59.2
        Element #9 = 57.5
    */

他の値を使用したインデックス作成

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; 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# プログラミング ガイド)」を参照してください。

参照

参照

インデクサー (C# プログラミング ガイド)

プロパティ (C# プログラミング ガイド)

概念

C# プログラミング ガイド