Индексаторы расширения в C# 15 завершают члены расширения в .NET 11 Preview 6
Индексаторы расширения появились в .NET 11 Preview 6 и позволяют добавлять доступ через this[...] к типам, которыми вы не владеете. Они завершают историю членов расширения, начатую методами и свойствами в C# 14.
.NET 11 Preview 6 вышел 2026-07-14, и в разделе C# примечаний к выпуску спрятана та часть, которая наконец делает члены расширения завершёнными: индексаторы расширения. Методы расширения существуют начиная с C# 3.0, свойства расширения появились вместе с блоком расширения в C# 14, а теперь вы можете добавить доступ через this[...] к типу, которым не владеете.
Чего не хватало до сих пор
Очевидным обходным путём всегда был обычный метод расширения. Если вам нужна была семантика индекса с конца для IReadOnlyList<T>, у которого нет индексатора, понимающего Index, вы писали что-то вроде этого:
public static class ReadOnlyListExtensions
{
public static T At<T>(this IReadOnlyList<T> list, Index index)
=> list[index.GetOffset(list.Count)];
}
// call site
IReadOnlyList<string> log = ["start", "work", "done"];
Console.WriteLine(log.At(^1)); // done
Console.WriteLine(log.At(^2)); // work
Это работает, но место вызова читается как log.At(^1) вместо синтаксиса log[^1], который даёт любой другой похожий на список тип. Вы теряете нотацию квадратных скобок, и теряете её также в списковых шаблонах.
Вариант с индексатором расширения
C# 15 позволяет объявить индексатор внутри блока extension ровно так же, как вы объявили бы индексатор экземпляра:
public static class ReadOnlyListExtensions
{
extension<T>(IReadOnlyList<T> list)
{
public T this[Index index] => list[index.GetOffset(list.Count)];
}
}
// call site
IReadOnlyList<string> log = ["start", "work", "done"];
Console.WriteLine(log[^1]); // done
Console.WriteLine(log[^2]); // work
Получатель, захваченный заголовком extension<T>(IReadOnlyList<T> list), находится в области видимости внутри тела, поэтому индексатор напрямую перенаправляет к list. Место вызова теперь использует настоящий синтаксис индексатора, и, поскольку компилятор трактует его как индексатор, он участвует в списковых шаблонах и выражениях диапазона так же, как встроенный.
Индексаторы расширения не ограничены формой с одним параметром и только чтением. Они поддерживают несколько параметров и аксессоры get и set, поэтому вы можете спроецировать двумерный поиск на плоское хранилище или предоставить доступную для записи проекцию над типом, который предлагает только доступ через методы.
Как включить
Это по-прежнему предварительная возможность языка, поэтому она не активируется в версии языка по умолчанию. Добавьте следующее в свой .csproj:
<LangVersion>preview</LangVersion>
Индексаторы расширения — последний из трёх классических видов членов, добравшийся до блоков расширения, а значит, ментальная модель теперь единообразна: методы, свойства и индексаторы объявляются одинаково внутри extension(...). Если вы прибегали к типам-обёрткам или неуклюжим вспомогательным методам .At(), чтобы обойти отсутствующий индексатор, Preview 6 — это выпуск, который позволяет их удалить. Полное описание смотрите в разделе C# примечаний к выпуску Preview 6.
Comments
Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.