C# Indexer Example

by Sam Allen - Updated January 7, 2010

You want to see an example program that uses the indexer property in the C# programming language to provide a clear way to access the internal elements of the class. Properties such as indexers often access a backing store, and with indexers you often accept a parameter of int type and access a backing store of array type. Here we look at an example of an indexer on a class in the C# language, which allows us to insert safety checks before directly accessing the private array, providing type-based encapsulation and some additional error-checking.

Using indexer on class

First here we look at a program text that contains a class that has an indexer member, which itself contains a get accessor and a set accessor. These accessors are implicitly used when you assign the class instance elements in the same way as you can assign elements in an array. The indexer provides a level of indirection where you can insert bounds-checking, and in this way you can improve reliability and simplicity with indexers.

--- Program that uses indexer with int (C#) ---

using System;

class Layout
{
    string[] _values = new string[100]; // Backing store

    public string this[int number]
    {
        get
        {
            // This is invoked when accessing Layout instances with the [ ].
            if (number >= 0 && number < _values.Length)
            {
                // Bounds were in range, so return the stored value.
                return _values[number];
            }
            // Return an error string.
            return "Error";
        }
        set
        {
            // This is invoked when assigning to Layout instances with the [ ].
            if (number >= 0 && number < _values.Length)
            {
                // Assign to this element slot in the internal array.
                _values[number] = value;
            }
        }
    }
}

class Program
{
    static void Main()
    {
        // Create new instance and assign elements in the array through the indexer.
        Layout layout = new Layout();
        layout[1] = "Frank Gehry";
        layout[3] = "I. M. Pei";
        layout[10] = "Frank Lloyd Wright";
        layout[11] = "Apollodorus";
        layout[-1] = "Error";
        layout[1000] = "Error";

        // Read elements through the indexer.
        string value1 = layout[1];
        string value2 = layout[3];
        string value3 = layout[10];
        string value4 = layout[11];
        string value5 = layout[50];
        string value6 = layout[-1];

        // Write the results.
        Console.WriteLine(value1);
        Console.WriteLine(value2);
        Console.WriteLine(value3);
        Console.WriteLine(value4);
        Console.WriteLine(value5); // Is null
        Console.WriteLine(value6);
    }
}

--- Output of the program ---

Frank Gehry
I. M. Pei
Frank Lloyd Wright
Apollodorus
(null)
Error

Indexer syntax description. This program text describes a compilation unit that includes two classes: a Layout class containing an indexer we define, and a Program class that contains the Main entry point. The Layout class contains an indexer that has get and set method bodies. The get and set accessors contain some logic that ensure the array will not will be accessed out-of-bounds. The array is termed the backing store for the indexer property in this example.

Main method output. The program text includes the Main entry point, which assigns elements in the Layout class backing store to various string literals. The string literals are the names of various famous architects from different times and places. The elements -1 and 1000 are used as parameters to the indexer, but these will not result in the backing store being changed because they are not valid. However, no exceptions are thrown by this because of our logic. Finally, the program displays the first four valid elements from the Layout class, and then an element that was not changed from null and then the result of an invalid access.

Many parameters

In this section we discuss how the parameter list of your indexer can vary depending on your requirements. In a collection that maps string keys to values, you will want to have an indexer that accepts a parameter of type string. This will make the class have a lookup function based on the indexer. If you are trying to simulate a two-dimensional collection, you can use two parameters in the indexer. Because indexers are actually regular methods in the implementation, there are not many limitations regarding the parameter types. However, ref and out parameters are not allowed.

Indexers in .NET Framework

Here we note that indexers have been used throughout the .NET Framework in Microsoft's own code since the framework was released. Almost all of the iterative collections and searchable collections use indexers as an option to access elements. This includes the Dictionary collection, which allows you to look up elements with the indexer. The indexer for the Dictionary often uses a string parameter as the indexer argument. The ArrayList and List classes also use indexers to simulate the built-in syntax of arrays in the C# language. This makes the List able to be used in syntactically the same way as an array when assigning or reading elements that are allocated.

(See Dictionary Examples, Keys and Values.)

(See Hashtable Use, Lookups and Examples.)

(See ArrayList Examples, Usage and Tips.)

(See List Examples.)

Intermediate language

Here we examine the intermediate language for the indexer shown in this article. You can see that a separate metadata table exists that stores the get_Item and set_Item methods, which implement the logic for the get and set accessors in the indexer. In the .NET Framework, the metadata implements properties in the same way as methods but with an additional table to provide more information about the type of methods. There should be no performance difference between an indexer access and a method that internally contain the same logic.

--- Intermediate language for example indexer (IL) ---

.property instance string Item
{
    .get instance string Layout::get_Item(int32)
    .set instance void Layout::set_Item(int32, string)
}

Summary

Here we looked at an example of the indexer type in the C# language, which is a property accessor type. Indexers have a somewhat more complicated syntax than other properties in the language, but the end result is they provide a function that is used when you do array-like accesses on the class instance. Indexers are very useful for helping describe and define intuitive types in a standard way through your class libraries. Additionally, they provide a useful level of indirection where you can insert bounds-checking, enhancing the reliability of array accesses throughout your program.

(Do not copy this page.)

Dot Net Perls | Search
Classes | Global Variable Use | Interface Program 1 | Sealed Class Test 1 | Static Constructor | This Instance
C# | Obsolete Attribute | True and False | Gradient Tips | Catch Examples
© 2010 Sam Allen. All rights reserved.
Dot Net Perls | Sam Allen