Export (0) Print
Expand All

Using Indexers (C# Programming Guide)

Indexers allow you to index a class, struct, or interface in the same way as an array. For more information about using indexers with an interface, see Interface Indexers.

To declare an indexer on a class or struct, use the this keyword, as in this example:

public int this[int index]    // Indexer declaration
{
    // get and set accessors
}

The type of an indexer and the type of its parameters must be at least as accessible as the indexer itself. For more information about accessibility levels, see Access Modifiers.

The signature of an indexer consists of the number and types of its formal parameters. It does not include the indexer type or the names of the formal parameters. If you declare more than one indexer in the same class, they must have different signatures.

An indexer value is not classified as a variable; therefore, it is not possible to pass an indexer value as a ref or out parameter.

To provide the indexer with a name that other languages can use, use a name attribute in the declaration. For example:

[System.Runtime.CompilerServices.IndexerName("TheItem")]
public int this [int index]   // Indexer declaration
{
}

This indexer will have the name TheItem. Not providing the name attribute would make Item the default name.

The following example shows how to declare a private array field, arr, and an indexer. Using the indexer enables direct access to the instance test[i]. The alternative to using the indexer is to declare the array as a public member and access its members, arr[i], directly.

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 };
    // Auto-Implemented Property
    System.DateTime date { get; set; } 
    // To enable client code to validate input 
    // when accessing your indexer.
    public int Length
    {
        get { return temps.Length; }
    }
    // Indexer declaration.
    // Input parameter is validated by client 
    // code before being passed to the indexer.
    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++)
        {
            // This example validates the input on the client side. You may
            // choose to validate it in the class that implements the indexer, and throw an
            // exception or return an error code in the case of invalid input.
            if (i < tempRecord.Length)
            {
                System.Console.WriteLine("Element #{0} = {1}", i, tempRecord[i]);
            }
            else
            {
                System.Console.WriteLine("Index value of {0} is out of range", i);
            }
        }

        //Uncomment this code to see how the .NET Framework handles indexer exceptions
        //try
        //{
        //    System.Console.WriteLine("Element #{0} = {1}", tempRecord[tempRecord.Length]);
        //}
        //catch (System.ArgumentOutOfRangeException e)
        //{
        //    System.Console.WriteLine(e);
        //}
    }
}

Element #0 = 0

Element #1 = 0

Element #2 = 0

Element #3 = 256

Element #4 = 0

Element #5 = 1024

Element #6 = 0

Element #7 = 0

Element #8 = 0

Element #9 = 0

Element #10 = 0

Notice that when an indexer's access is evaluated, for example, in a Console.Write statement, the get accessor is invoked. Therefore, if no get accessor exists, a compile-time error occurs.

C# does not limit the index type to integer. For example, it may be useful to use a string with an indexer. Such an indexer might be implemented by searching for the string within the collection, and returning the appropriate value. As accessors can be overloaded, the string and integer versions can co-exist.

In this example, a class is declared that stores the days of the week. A get accessor is declared that takes a string, the name of a day, and returns the corresponding integer. For example, Sunday will return 0, Monday will return 1, and so on.

// 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)
    {
        int i = 0;
        foreach (string day in days)
        {
            if (day == testDay)
            {
                return i;
            }
            i++;
        }
        return -1;
    }

    // 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"]);
        System.Console.WriteLine(week["Made-up Day"]);
    }
}

5

-1

There are two main ways in which the security and reliability of indexers can be improved:

  • Always ensure that your code performs range and type checks when setting and retrieving values from any buffer or array accessed by the indexers.

  • Set the accessibility of the get and set accessors to be as restrictive as is reasonable. This is important for the set accessor in particular. For more information, see Asymmetric Accessor Accessibility (C# Programming Guide).

Community Additions

ADD
Show:
© 2014 Microsoft