C# tutorials > Core C# Fundamentals > Object-Oriented Programming (OOP) > What are indexers?

What are indexers?

Indexers are a language feature in C# that allow instances of a class or struct to be accessed like arrays, using square brackets ([]). They provide a way to encapsulate access to a collection of data within an object, making the code more readable and maintainable. Think of them as smart arrays or dictionaries that can have custom logic for setting and getting values.

Basic Indexer Syntax

The basic syntax of an indexer involves using the this keyword followed by square brackets containing the index type (usually an integer). It includes get and set accessors, similar to properties. The get accessor retrieves the value at the specified index, while the set accessor allows you to assign a value to that index.

In this example, MyClass holds an array of strings. The indexer allows you to access the array using myClassInstance[index]. Error handling is crucial within the get and set accessors to prevent out-of-bounds exceptions.

public class MyClass
{
    private string[] names = new string[10];

    public string this[int index]
    {
        get
        {
            if (index >= 0 && index < names.Length)
            {
                return names[index];
            }
            else
            {
                return null; // Or throw an exception
            }
        }
        set
        {
            if (index >= 0 && index < names.Length)
            {
                names[index] = value;
            }
        }
    }
}

Concepts Behind the Snippet

Encapsulation: Indexers encapsulate the internal data structure (e.g., the names array) and provide a controlled interface for accessing it.

Abstraction: They abstract away the underlying implementation details of how the data is stored and retrieved.

Operator Overloading: Technically, indexers are a form of operator overloading, specifically overloading the [] operator.

Real-Life Use Case Section

Data Structures: Indexers are commonly used when creating custom collection classes, such as matrices, dictionaries, or custom lists.

Database Access: You might use indexers to access records in a dataset or a database table, where the index represents a row number or a unique identifier.

Configuration Settings: Indexers can provide a convenient way to access configuration settings by name.

Example: Consider a class that represents a student record. You could use an indexer to access student information by student ID: studentRecord[studentId]

Best Practices

Validation: Always validate the index within the get and set accessors to prevent out-of-bounds errors.

Exception Handling: Throw appropriate exceptions (e.g., IndexOutOfRangeException) when an invalid index is encountered.

Readability: Keep the logic within the get and set accessors simple and focused. Avoid complex computations within these blocks.

Use meaningful index types: While integers are common, consider using strings or enums as index types when they better represent the data being accessed.

Interview Tip

Be prepared to explain the difference between properties and indexers. Properties expose specific attributes of an object, while indexers provide array-like access to a collection of data within the object.

Also, discuss the advantages of using indexers, such as improved readability, encapsulation, and flexibility in data access.

When to Use Them

Use indexers when you want to provide array-like access to a collection of data within a class or struct.

They are particularly useful when you need to encapsulate the underlying data structure and control how data is accessed and modified.

Avoid using indexers if a simple property can adequately represent the data you need to expose.

Memory Footprint

Indexers themselves don't directly add to the memory footprint of an object. The memory footprint is determined by the data structure that the indexer accesses (e.g., the names array in the example).

Be mindful of the memory usage of the underlying data structure, especially when dealing with large collections.

Alternatives

Properties: Use properties when you want to expose specific attributes of an object, rather than providing array-like access to a collection.

Methods: Use methods when you need to perform complex operations on the data within an object. For example, a method might perform a search or a calculation based on the data.

Standard Collection Classes: If you're working with a standard collection of data (e.g., a list, dictionary, or array), consider using the built-in collection classes provided by the .NET Framework. They offer a wide range of features and are often more efficient than implementing your own custom collection with indexers.

Pros

Improved Readability: Indexers make code more readable by providing a natural and intuitive way to access data.

Encapsulation: They encapsulate the underlying data structure and provide a controlled interface for accessing it.

Flexibility: They allow you to customize the logic for accessing and modifying data.

Cons

Complexity: Implementing indexers can add complexity to your code, especially when dealing with complex data structures.

Overuse: Avoid using indexers when a simpler approach, such as properties or methods, would suffice.

FAQ

  • Can an indexer have multiple parameters?

    Yes, an indexer can have multiple parameters. This allows you to create more complex indexing schemes, such as accessing elements in a two-dimensional matrix using two indices: myMatrix[row, column]. The parameters are specified within the square brackets.
  • Can an indexer be overloaded?

    Yes, you can overload indexers by providing multiple indexers with different parameter types. This allows you to access data in different ways depending on the provided arguments. For example, you could have one indexer that accepts an integer index and another that accepts a string key.
  • Are indexers inherited?

    No, indexers are not inherited in the traditional sense. However, derived classes can hide indexers from the base class using the new keyword, or they can implement new indexers with the same signature, effectively shadowing the base class indexer.
  • What are the limitations of Indexers?

    Indexers cannot be declared as static. This is because indexers are instance members and are associated with a specific object instance. Also you can't use indexers in interfaces.