In C# an indexer provides array-like syntax. It allows a type to be accessed the same way as an array. Properties such as indexers often access a backing store.
In an indexer, we often accept an int
parameter and access a backing array. We can initialize values in an indexer with an initializer. It is also possible to accept other types like strings.
This program contains a class
that has an indexer member, which itself contains a get accessor and a set accessor. These accessors are implicitly used.
class
with an indexer. Layout contains an indexer that has get and set method bodies.out
-of-bounds.class
. It shows the result of an invalid access.using System; class Layout { string[] _values = new string[100]; // Backing store public string this[int number] { get { // This is invoked when accessing Layout 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 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); } }Frank Gehry I. M. Pei Frank Lloyd Wright Apollodorus (null) Error
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.
.property instance string Item { .get instance string Layout::get_Item(int32) .set instance void Layout::set_Item(int32, string) }
This program uses an indexer member on an interface
type. The interface
declares the indexer and leaves the get and set accessors empty.
class
declares an indexer with the same parameters. You can use the indexer through the interface
type.IPerl
type is an interface
. The Implementation type implements IPerl
and provides data for the indexer accessors.using System; interface IPerl { int this[int number] { get; set; } } class Implementation : IPerl { int[] _data = { 0, 10, 20, 30 }; // Default values public int this[int number] { get { // Get accessor implementation. return this._data[number]; } set { // Set accessor implementation. this._data[number] = value; } } } class Program { static void Main() { // Create an object instance. // ... Use the indexer through the interface type. IPerl perl = new Implementation(); Console.WriteLine(perl[0]); Console.WriteLine(perl[1]); Console.WriteLine(perl[2]); Console.WriteLine(perl[3]); // Use set accessor. perl[0] = -1; Console.WriteLine(perl[0]); } }0 10 20 30 -1
An indexer can be initialized just like other properties. We specify the square brackets around the index and assign it to a value. The syntax is unusual at first glance.
class
. This class
stores a string
array of animals (strings) as a field.this
-property is an indexer. In the Farm initializer, we create entries at indexes 1, 3 and 5 for animal names.using static System.Console; class Farm { string[] _animals = new string[10]; public string this[int number] { get { return _animals[number]; } set { _animals[number] = value; } } } class Program { static void Main() { // ... Use an index initializer. Farm f = new Farm() {[1] = "cat",[3] = "bird",[5] = "dog" }; // Get values from farm by index. WriteLine(f[1]); WriteLine(f[3]); WriteLine(f[5]); } }cat bird dog
This example uses an indexer with the "expression-bodied" syntax. This is more like a lambda expression.
using static System.Console; class Apartment { string[] _furniture = new string[10]; public string this[int number] { get => _furniture[number]; // Expression-bodied indexer. set => _furniture[number] = value; } } class Program { static void Main() { var apt = new Apartment() { [0] = "chair", [1] = "bed", [2] = "desk" }; // Use indexer. WriteLine(apt[0]); WriteLine(apt[1]); WriteLine(apt[2]); } }chair bed desk
String
parameterThe parameter list varies depending on your requirements. In a collection that maps string
keys to values, you will want to have an indexer that accepts a string
.
using System; class Example { public int this[string key] { get { if (key == "bird") { return 0; } else if (key == "cat") { return 1; } else { return 2; } } } } class Program { static void Main() { // Use class with string indexer. var e = new Example(); Console.WriteLine(e["bird"]); Console.WriteLine(e["cat"]); Console.WriteLine(e["?"]); } }0 1 2
An indexer is a property accessor type. Indexers have more complex syntax than other properties. They provide array-like accesses on the class
instance.