Indexer extension members in C# 15

September 11th 2026 C# 15 C# .NET

Last year, extension members were the biggest new feature of C# 14. I wrote about them in detail in multiple blogposts. This year, extension indexers are being added to C# 15 which didn't make it into C# 14.

Extension methods have been added to C# as a part of LINQ a long time ago. In essence, they were static methods which could be called as if they were members of the type of their first parameter:

public static string? FirstCharToUpper(this string? receiver)
{
    return string.IsNullOrEmpty(receiver)
        ? receiver
        : string.Concat(receiver[..1].ToUpper(), receiver.AsSpan(1));
}

They had to be placed in a static class and the first parameter had to be introduced with this keyword. Such a method could be called as a member method of its first parameter which therefore didn't have to be passed in as a parameter:

"me".FirstCharToUpper()

The extension members feature of C# 14 added support for adding other member types as extensions. To make this possible, the syntax for declaring them had to be different from the existing extension method syntax. Their syntax is more similar to regular type members, but they have to be inside an extension block, in a static class. The old syntax is still supported for extension instance methods, but a new one was added to match the syntax of other extension member types:

extension(string? receiver)
{
    public string? FirstCharToUpper()
    {
        return string.IsNullOrEmpty(receiver)
            ? receiver
            : string.Concat(receiver[..1].ToUpper(), receiver.AsSpan(1));
    }
}

Other supported member types were:

  • instance properties:
    extension(string? receiver)
    {
        public bool IsEmptyField =>
            string.IsNullOrEmpty(receiver) || receiver == "N/A";
    }
    
  • static methods:

    extension(string?)
    {
        public static string? Create(string? pattern, int count)
        {
            if (pattern == null)
            {
                return null;
            }
    
            if (pattern.Length == 0 || count <= 0)
            {
                return string.Empty;
            }
    
            var builder = new StringBuilder(pattern.Length * count);
            for (var i = 0; i < count; i++)
            {
                builder.Append(pattern);
            }
    
            return builder.ToString();
        }
    }
    
  • static properties:

    extension(string?)
    {
        public static string NotAvailable => "N/A";
    }
    
  • operators:
    extension(string?)
    {
        public static string? operator *(string? pattern, int count)
        {
            return string.Create(pattern, count);
        }
    }
    

In addition to all these extension member types, C# 15 now also supports extension indexers. The syntax follows the pattern of all other extension members:

extension(IEnumerable<int> sequence)
{
    public int this[int index] => sequence.ElementAt(index);
}

Just like other extension member types, extension indexers are only considered by the compiler when there are no matching actual member indexers. Even implicit Index and Range indexers have precedence over extension indexers with the same type. E.g., if a type has an int indexer and a Length or Count property, then the Index indexer is implicitly supported and an extension Index indexer will be ignored even if you implement it.

In addition to all that, extension indexers for arrays and strings will be ignored altogether as documented at the very bottom in the collapsed section of the feature specification:

The current spec and baseline rules for array access and string access mean that extension indexers don't work on arrays or strings.
Yet the declaration of such extension indexers is permitted.

Extension indexers are currently a preview feature and might still change. To use them in .NET 11 preview 7, you must set the language version to preview in your project:

<PropertyGroup>
  <LangVersion>preview</LangVersion>
</PropertyGroup>

A sample project with all supported extension member types (the ones from C# 14 and the indexers newly added in C# 15) is available in my GitHub repository. Feel free to clone it and try out the feature yourself.

With the addition of extension indexers in C# 15, the originally planned scope of extension members from C# 14 has been completed. The only remaining member type without extension support are conversion operators which aren't even planned for now as stated in the feature specification:

Does not cover user-defined implicit and explicit conversion operators, which are not yet designed or planned.

Get notified when a new blog post is published (usually every Friday):

Copyright
Creative Commons License