How to work with indexers in C#

Take advantage of indexers in C# to access an instance of a class or a struct by using indexes

Indexer

Indexer

The C# programming language includes support for indexers -- a feature that enables you to use an object just as an array. Indexers are also known as smart arrays and can be defined similar to how a property is defined. The MSDN states: "Indexers allow instances of a class or struct to be indexed just like arrays. Indexers resemble properties except that their accessors take parameters."

Although indexers and properties have similarities in more ways than one, there are subtle differences between them. Unlike properties, you can access an indexer using indexes. Remember that you need to access a property by using its name. Also, indexers are instance members of a class and hence they cannot be static. You can have both static and non-static properties.

The following code snippet illustrates how an indexer is declared:

<Modifier> <Return type> this [argument list]

{

  get

  {

  }

  Set

  {

  }

}

Note that the modifier as shown in the syntax declaration of an indexer can be private, public, protected or internal.

Consider the following class:

public class Contact

    {

        private string[] address = new string[3];

        public string this[int index]

        {

            get

            {

                return address[index];

            }

            set

            {

                address[index] = value;

            }

        }

    }

The Contact class contains a private member named address and defines an indexer. The address member is an array of type string. Here's how you can create an instance of the Contact class and use the indexer.

Contact contact = new Contact();

 contact[0] = "Begumpet";

 contact[1] = "Hyderabad";

 contact[2] = "Telengana";

for (int i = 0; i < 3; i++)

Console.WriteLine (contact[i]);

It should be noted that you need to use the "this" keyword to define indexers. Note that you are not constrained to using only integers as indexes to access indexers -- you can even use other lookup mechanisms as well. An indexer is typically used when your class represents a collection or objects. You can then use indexer to access a specific element using index.

Let's try an example. Consider the following class named Customer.

public class Customer

    {

       public List<Order> Orders

        {

            get; set;

        }

       public Order this[int orderID]

        {

            get

            {

                return (from o in Orders

                        where o.OrderID == orderID

                        select o).First();

            }

        }

    }

The Customer class defines an indexer of type Order. It also contains a public property that is a list of type Order. Here's the Order class for your reference.

public class Order

    {

        public int OrderID

        {

            get; set;

        }

    }

The following code snippet illustrates how you can access the indexer of the Customer class to retrieve a particular order.

   List<Order> lstOrder = new List<Order>();

  Order o1 = new Order();

  o1.OrderID = 1;

  Order o2 = new Order();

  o2.OrderID = 2;           

  lstOrder.Add(o1);

  lstOrder.Add(o2);

  Customer customer = new Customer();

  customer.Orders = lstOrder;

  Order o = customer[1];

Refer to the code snippet above. Note how a generic list of type Order has been created and assigned to the Orders property of an instance of the Customer class. Next, you just pass the OrderId as a parameter to retrieve the particular order instance.

Indexers support inheritance, can be polymorphic and can also be abstract. Consider the following class that defines an indexer that is virtual. The ContactBase class is modified version of the Contact class we discussed earlier in this article.

 public class ContactBase

    {

        protected string[] address = new string[3];

        public virtual string this[int index]

        {

            get

            {

                return address[index];

            }

            set

            {

                address[index] = value;

            }

        }

    }

You can now derive a class from the ContactBase class and override the indexer as shown below.

public class ConcreteContact: ContactBase

    {

       public override string this[int index]

        {

            get

            {

                return address[index];

            }

            set

            {

                address[index] = value;

            }

        }

    }

So, in the code example above, we explored how indexers can be used while inheriting types and how they can show polymorphic behavior.

You can just as well define an indexer as abstract. To do this, you need to create an abstract class and then define an indexer as abstract inside it. Let's modify the ContactBase class and define the indexer as abstract. Here's how the modified version of the ContactBase class would now look:

 public abstract class ContactBase

    {

        protected string[] address = new string[3];

        public abstract string this[int index]

        {

            get; set;

        }

}

You don't need to change the ConcreteContact class anyway. You can now leverage the indexer to assign string values to an instance of the ConcreteContact class as shown below.

ConcreteContact contact = new ConcreteContact();

contact[0] = "Begumpet";

contact[1] = "Hyderabad";

contact[2] = "Telengana";

This article is published as part of the IDG Contributor Network. Want to Join?

To comment on this article and other InfoWorld content, visit InfoWorld's LinkedIn page, Facebook page and Twitter stream.
From CIO: 8 Free Online Courses to Grow Your Tech Skills
Notice to our Readers
We're now using social media to take your comments and feedback. Learn more about this here.