C# Programming Guide
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
}
Remarks

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.

Example 1

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.

C#
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);
        //}
    }
}
Output

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.

Indexing Using Other Values

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.

Example 2

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.

C#
// 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"]);
    }
}
Output

5

-1

Robust Programming

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).

See Also

Tasks

Indexers Sample

Reference

Indexers (C# Programming Guide)
Properties (C# Programming Guide)

Concepts

C# Programming Guide

Tags :


Community Content

Shahar Prish - MSFT
About the first indexer sample

The first indexer example given, while correct technically, is not how one usually writes collections.

 

Returning 0 (zero) in OOB (out of bounds) condition is considered, for the most part, pretty bad behavior as it does not really allow users to know if they are accessing the collection outside of its bounds.

What would constitute "good behavior" in most cases of this example is to simply call into the array and let it throw. That way, developers do not accidently miss error conditions.

On top of that, instead of using a naked 100 when checking the index, it would have been better to check the .Length property of the array so that if the code changes, there's less chance of making an error.

This is not to say that there aren't cases where this collection would make sense - fallback collections can be usable - it's just that it's not a mainstream example.

Tags :

kempofighter1874
Here is a compilable form of example 1 along with the correct expected output
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.
// 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);
}
}
System.Console.WriteLine("Press any key to exit!");
int x = System.Console.Read();
   //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);
//}
}
}

Now the output should look something like this:

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

Tags : contentbug

Page view tracker