Индексаторы в C#: делаем объекты доступными как массивы
Что такое индексатор
Индексатор (Indexer) — специальный член класса/структуры, позволяющий обращаться к экземпляру как к массиву: obj[index]. Объект ведёт себя как виртуальный массив.
Синтаксис
public ТипЭлемента this[ТипИндекса индекс]
{
get { /* вернуть значение по индексу */ }
set { /* установить значение по индексу */ }
}
- Используется ключевое слово
thisвместо имени - Может быть
get,setили оба - Параметров может быть несколько (многомерные индексаторы)
- Тип индекса не обязательно
int— может бытьstring,enum, любой тип
Пример: индексатор по строке (имена)
public class IndexedNames
{
private string[] _names = new string[10];
public static int Size = 10;
public IndexedNames()
{
for (int i = 0; i < Size; i++) _names[i] = "N.A.";
}
public string this[int index]
{
get
{
if (index >= 0 && index < Size) return _names[index];
return ""; // или throw new IndexOutOfRangeException()
}
set
{
if (index >= 0 && index < Size) _names[index] = value;
}
}
// Перегруженный индексатор по строке — поиск индекса по имени
public int this[string name]
{
get
{
for (int i = 0; i < Size; i++)
if (_names[i] == name) return i;
return -1; // не найдено
}
}
}
// Использование
var names = new IndexedNames();
names[0] = "Zara";
names[1] = "Riz";
names[2] = "Nuha";
for (int i = 0; i < IndexedNames.Size; i++)
Console.WriteLine(names[i]); // через int индексатор
Console.WriteLine(names["Nuha"]); // 2 — через string индексатор
Перегруженные индексаторы
Можно объявить несколько индексаторов с разными сигнатурами (тип/число параметров):
public class Matrix
{
private double[,] _data;
// Двумерный индексатор
public double this[int row, int col]
{
get => _data[row, col];
set => _data[row, col] = value;
}
// Индексатор по кортежу (C# 7+)
public double this[(int Row, int Col) rc]
{
get => _data[rc.Row, rc.Col];
set => _data[rc.Row, rc.Col] = value;
}
}
Индексаторы в интерфейсах
public interface IRepository
{
T this[int id] { get; set; }
}
public class UserRepository : IRepository
{
private Dictionary _users = new();
public User this[int id]
{
get => _users.TryGetValue(id, out var u) ? u : null;
set => _users[id] = value;
}
}
Свойства vs Индексаторы
| Характеристика | Свойство | Индексатор |
|---|---|---|
| Идентификация | По имени | По сигнатуре параметров (this[]) |
| Параметры | Нет | Обязательно хотя бы один |
| Статические | Могут быть static | Не могут быть static |
| Назначение | Доступ к одному значению/состоянию | Доступ к элементам коллекции/структуры |
| Наследование | Virtual/override | Virtual/override |
Expression-bodied индексаторы (C# 7.0+)
public class BitVector
{
private ulong _bits;
public bool this[int index]
{
get => (_bits & (1UL << index)) != 0;
set
{
if (value) _bits |= 1UL << index;
else _bits &= ~(1UL << index);
}
}
}
Лучшие практики
- Индексаторы должны вести себя интуитивно — как доступ к коллекции
- Валидируйте индексы: выбрасывайте
IndexOutOfRangeExceptionилиArgumentOutOfRangeExceptionдля некорректных значений - Для поиска по строке лучше использовать метод (
FindByName) — индексатор подразумевает O(1) доступ - Рассмотрите реализацию
IEnumerable<T>для поддержкиforeach - Не злоупотребляйте — если семантика не «коллекция», лучше используйте методы
Язык C
- C# Hello World — создание вашего первого C# приложения
- Понимание ключевых слов и идентификаторов C#: правила, списки и лучшие практики
- Мастерство C# переменных и примитивных типов данных: полное руководство
- Операторы C# — исчерпывающее руководство по операторам в C#
- Освоение побитовых и сдвиговых операторов в C#: полное руководство
- Основы ввода-вывода в C#: Console.WriteLine, ReadLine и форматирование
- Выражения, операторы и блоки в C#: подробное руководство с примерами
- Освоение комментариев в C#: виды, лучшие практики и XML-документация
- Освоение оператора switch в C#: синтаксис, примеры и лучшие практики
- Тернарный оператор в C#: краткое руководство по условным выражениям