C# This Indexer: Syntax, Usage, and Tradeoffs
c# this indexer: Learn how to declare and use C# indexers with the this keyword, including syntax, overloads, read-only access, and performance tradeoffs.
The c# this indexer syntax is the way to give a class array-like access to its internal data. Instead of exposing a collection property and forcing callers to work through that property, an indexer lets you write instance[key] directly. This article explains how to declare indexers, what parameters they accept, and where they make sense in production code.
What an Indexer Does
An indexer is a member that allows an instance of a class or struct to be indexed like an array. The this keyword in the declaration is not a reference to the current instance in the usual sense; it is the syntax that marks the member as an indexer. The compiler treats it as a special property that takes one or more parameters.
Declaring an Indexer with the this Keyword
The basic declaration uses the this keyword followed by a parameter list in square brackets. The accessors get and set define how the indexed value is read and written.
public class StringCollection { private readonly List<string> _items = new(); public string this[int index] { get => _items[index]; set => _items[index] = value; } }
The get accessor returns the value at the given index, and the set accessor assigns to it. The value keyword represents the right-hand side of an assignment.
Indexer Parameters and Overloads
An indexer can accept more than one parameter and parameters of any type. This is useful for multidimensional or key-based access.
public class Matrix { private readonly double[,] _data; public double this[int row, int column] { get => _data[row, column]; set => _data[row, column] = value; } }
You can also overload indexers by parameter type, as long as the signatures differ.
public class Lookup { public string this[string key] { get; set; } public string this[int id] { get; set; } }
Read-Only and Write-Only Indexers
If you omit the set accessor, the indexer is read-only. If you omit get, it is write-only. This is the same rule as properties. A read-only indexer is common when the underlying collection is immutable or when you want to expose only a projection.
public class ReadOnlyList { private readonly int[] _values = { 1, 2, 3 }; public int this[int index] => _values[index]; }
Common Use Cases for Indexers
Indexers are most useful when you want to provide a natural collection-like interface without exposing the internal storage. They are common in custom collection classes, adapter wrappers, and data access layers. For example, a repository might expose an indexer that looks up an entity by its primary key.
public class CustomerRepository { private readonly Dictionary<int, Customer> _customers; public Customer this[int id] => _customers[id]; }
Performance and Maintainability Considerations
Indexers are not inherently slower than methods, but they can hide complexity. Every call goes through the accessor, so any validation or transformation you put inside the accessor runs on each access. If the accessor performs expensive work, such as a database query, be aware that the cost is incurred each time the indexer is used.
From a maintainability perspective, keep indexer logic simple. If the accessor becomes more than a few lines, consider whether a method with a descriptive name would be clearer. Indexers are best for direct, array-like access; they are not a substitute for well-named methods that express intent.
Indexers vs. Properties vs. Methods
| Member type | Syntax | Use case |
|---|---|---|
| Property | public string Name { get; set; } | Single value with a name |
| Indexer | public string this[int i] { get; set; } | Collection-like access by key or index |
| Method | public string GetByIndex(int i) | Operation that may have side effects or multiple steps |
The choice depends on how callers will naturally use the type. If you want obj[0] to read like an array, use an indexer. If you want obj.GetById(0) to read like a query, use a method.
Edge Cases and Limitations
Indexers cannot be static. They must be instance members. Also, you cannot use ref or out parameters in an indexer. The parameter list must contain at least one parameter, and the indexer cannot be used as a method group.
Another limitation is that the indexer's parameter types are not checked at compile time if you use a non-generic collection, but with a generic collection you get type safety. For example, this[int index] will only accept integers, while this[string key] will only accept strings.
When to Avoid Indexers
If the class has multiple ways to look up data, overloading indexers can become confusing. For example, having both this[int id] and this[string name] might make the intent unclear. In such cases, named methods like GetById and GetByName are often better. Indexers are also a poor fit when the accessor has significant side effects or requires complex validation that is better expressed as a method.