Open In App

C# Indexers

Last Updated : 16 Oct, 2025
Comments
Improve
Suggest changes
31 Likes
Like
Report

In C#, an indexer allows objects of a class or struct to be accessed using array-like syntax. When an indexer is defined, the object can be indexed using the array access operator ([ ]), just like arrays or collections.

Indexers make classes behave like virtual arrays, enabling users to retrieve or assign values directly through indices rather than using explicit method calls.

Syntax

[access_modifier] [return_type] this[parameter_list]

{

get { // code to return value }

set { // code to assign value }

}

  • access_modifier: Specifies accessibility (e.g. public, private, protected, internal)
  • return_type: Defines the type of value the indexer returns
  • this: Refers to the current instance of the class
  • parameter_list: Specifies the index parameters used to access class elements
  • get / set: Accessors used to read and assign values respectively

Main difference between Indexers and Properties:

  • A property accessor (get/set) does not take parameters.
  • An indexer accessor (get/set) must take at least one parameter.

Example: Simple Indexer

C#
using System;

class Geeks
{
    private string[] values = new string[3];

    // Indexer declaration
    public string this[int index]
    {
        get
        {
            return values[index];
        }
        set
        {
            values[index] = value;
        }
    }
}

class Program
{
    static void Main()
    {
        Geeks obj = new Geeks();

        obj[0] = "C";
        obj[1] = "C++";
        obj[2] = "C#";

        Console.WriteLine("First Value: " + obj[0]);
        Console.WriteLine("Second Value: " + obj[1]);
        Console.WriteLine("Third Value: " + obj[2]);
    }
}

Output
First value: C
Second value: C++
Third value: C#

The class Geeks defines an indexer that enables direct access to elements through array-like syntax (obj[0], obj[1], etc.).

Types of Indexers

In C#, indexers can be classified based on the number of parameters they accept. They allow objects to be accessed using array-like syntax, making them intuitive and flexible for data manipulation.

1. Single-Dimensional Indexer

  • A single-dimensional indexer takes one parameter, typically an integer similar to how elements are accessed in a one-dimensional array.
  • It is the most common form of indexer used to represent sequential data like lists or collections.

Syntax:

C#
class Sample
{
    private int[] numbers = new int[5];

    public int this[int index]
    {
        get { return numbers[index]; }
        set { numbers[index] = value; }
    }
}

Usage:

C#
Sample obj = new Sample();
obj[0] = 10;
obj[1] = 20;

Console.WriteLine(obj[0]); // Output: 10
Console.WriteLine(obj[1]); // Output: 20
  • Uses one index parameter.
  • Provides simple array-like access to class elements.
  • Commonly used for custom collection classes or wrapper types.

2. Multi-Dimensional Indexer

  • A multi-dimensional indexer takes multiple parameters, similar to multi-dimensional arrays (e.g., 2D or 3D arrays).
  • This type of indexer is useful for classes that represent tabular or grid-like data structures.

Syntax:

C#
class Matrix
{
    private int[,] data = new int[3, 3];

    public int this[int row, int col]
    {
        get { return data[row, col]; }
        set { data[row, col] = value; }
    }
}

Usage:

C#
Matrix matrix = new Matrix();
matrix[0, 1] = 5;
matrix[2, 2] = 9;

Console.WriteLine(matrix[0, 1]); // Output: 5
Console.WriteLine(matrix[2, 2]); // Output: 9
  • Takes two or more parameters.
  • Enables indexed access for complex data structures such as matrices or coordinate maps.
  • Each parameter represents a dimension, e.g., row and column in a 2D structure.

Implementing Indexers

1. Multi-Parameter Indexers

Indexers can take multiple parameters, enabling access to data through complex indexing.

C#
using System;

public class Indexer
{
    private int[] data = new int[10];

    // Indexer with multiple parameters
    public int this[int index, bool square]
    {
        get
        {
            if (square)
                return data[index] * data[index];
            else
                return data[index];
        }
        set
        {
            if (square)
                data[index] = (int)Math.Sqrt(value);
            else
                data[index] = value;
        }
    }
}

class Program
{
    static void Main()
    {
        Indexer obj = new Indexer();

        obj[0, false] = 5;
        obj[1, false] = 10;

        Console.WriteLine(obj[0, false]); // 5
        Console.WriteLine(obj[1, true]);  // 100
    }
}

Output
5
100

2. Overloading Indexers

Just like methods, indexers can be overloaded using different parameter types or counts.

C#
public class Example
{
    private int[] data = new int[3] { 10, 20, 30 };

    // Integer indexer
    public int this[int index]
    {
        get { return data[index]; }
    }

    // String indexer
    public int this[string position]
    {
        get
        {
            return position.ToLower() switch
            {
                "first" => data[0],
                "last" => data[data.Length - 1],
                _ => throw new ArgumentException("Invalid index name")
            };
        }
    }
}

class Program
{
    static void Main()
    {
        Example ex = new Example();
        Console.WriteLine(ex[0]);       // 10
        Console.WriteLine(ex["last"]);  // 30
    }
}

3. Read-Only Indexers

If only a get accessor is defined, the indexer becomes read-only.

C#
public class ReadOnlyExample
{
    private int[] arr = { 1, 2, 3, 4 };

    public int this[int index]
    {
        get { return arr[index]; }
    }
}

class Program
{
    static void Main()
    {
        ReadOnlyExample r = new ReadOnlyExample();
        Console.WriteLine(r[2]); // 3
    }
}

4. Indexers in Interfaces

Indexers can be declared in interfaces and implemented by classes.

C#
public interface IContainer
{
    int this[int index] { get; set; }
}

public class Container : IContainer
{
    private int[] data = new int[5];

    public int this[int index]
    {
        get { return data[index]; }
        set { data[index] = value; }
    }
}

class Program
{
    static void Main()
    {
        IContainer c = new Container();
        c[0] = 42;
        Console.WriteLine(c[0]); // 42
    }
}

If multiple interfaces have indexers with the same signature, you can use explicit implementation to avoid conflicts.

5. Indexers in Collection Classes

Custom collection classes often use indexers to provide direct element access.

C#
using System.Collections.Generic;
using System;

public class StudentCollection
{
    private List<string> students = new List<string>();

    public void Add(string name) => students.Add(name);

    public string this[int index]
    {
        get { return students[index]; }
        set { students[index] = value; }
    }
}

class Program
{
    static void Main()
    {
        StudentCollection sc = new StudentCollection();
        sc.Add("Amit");
        sc.Add("Neha");

        Console.WriteLine(sc[0]); // Amit
        sc[1] = "Riya";
        Console.WriteLine(sc[1]); // Riya
    }
}

Important Points

  • Indexers are instance members, not static.
  • The this keyword is always used to define an indexer.
  • The value keyword represents the value assigned in the set accessor.
  • Indexers are also known as Parameterized Properties or Smart Arrays.
  • A class can have multiple indexers differentiated by parameter signature.
  • Indexers can be overloaded, read-only or interface-based.
  • Indexers improve usability and readability of collection-like objects.

Article Tags :

Explore