# C# Language Design Meeting for February 2nd, 2026

## Agenda

- [Extension indexers](#extension-indexers)
    - [Open question: params on indexer setters](#open-question-params-on-indexer-setters)
    - [Open question: should extension Length/Count properties make a type countable?](#open-question-should-extension-lengthcount-properties-make-a-type-countable)
    - [Open question: extension indexer access ordering](#open-question-extension-indexer-access-ordering)

## Quote of the Day

- "Does Count count?" "Only after a Lengthy discussion"

## Discussion

### Extension indexers

Champion issue: https://github.com/dotnet/csharplang/issues/9856  
Spec: https://github.com/dotnet/csharplang/blob/7a58785d96afc903219cb33f45360744211c3c4c/proposals/extension-indexers.md

The LDM reviewed the extension indexers proposal and had no comments on the overview.

#### Open question: params on indexer setters

Open question: https://github.com/dotnet/csharplang/blob/7a58785d96afc903219cb33f45360744211c3c4c/proposals/extension-indexers.md#dealing-with-params

When an extension indexer has a `params` parameter, the setter's implementation method ends up with `params` in a
non-final position (the value parameter comes after it). The question was whether to disallow this scenario or simply
emit the `params` attribute as-is.

This isn't actually an extension-specific problem: regular instance indexers with `params` already emit setters with the
`params` attribute in the middle. The difference is that for regular indexers, users can't directly invoke the accessor
methods, so they never observed this signature (though there is a possibility it can be observed through C# consuming a
VB non-default indexed property). With extension indexers, the implementation methods are invokable via the disambiguation
syntax, so it can be more directly observed.

Both the C# and VB compilers are designed to ignore `params` attributes on any parameter other than the last. While
we should verify that tooling (like IDE signature help) doesn't display `params` in confusing locations, the consensus
was that we should simply let things fall out naturally without special-casing this scenario.

##### Conclusion

Emit the `params` attribute on the setter implementation method even though it appears on a non-final parameter.
Compilers will ignore it in that position, maintaining consistency with how regular indexers already behave. We will
ensure that the IDE behaves well in these scenarios.

#### Open question: should extension Length/Count properties make a type countable?

Open question: https://github.com/dotnet/csharplang/blob/7a58785d96afc903219cb33f45360744211c3c4c/proposals/extension-indexers.md#should-extension-lengthcount-properties-make-a-type-countable

In C# 14, extension properties were explicitly excluded from contributing to implicit indexers (the pattern-based
indexing that uses `Length`/`Count` with an `int` indexer or `Slice` method). This was because extension methods also
didn't contribute to that pattern.

However, now that we're adding extension indexers that can directly take `System.Index` or `System.Range`, a question
arises: if you define an extension indexer taking `Index` and an extension `Length` property, should list patterns work
on that type?

```cs
class C { }

static class E
{
    extension(C c)
    {
        object this[System.Index] => ...;
        C this[System.Range] => ...;
        int Length => ...;
    }
}

// Should this work?
if (c is [.., var y]) { }
```

There was strong sentiment in the LDM for a "maximalist" approach: extension members should participate in language
patterns wherever possible. This mostly matches our past approaches, and how we've loosened similar restrictions on
other language features recently.

We considered whether to require that all members contributing to a pattern come from the "same origin" (same extension
block or all from the instance type). The rationale was to prevent confusing situations where unrelated extensions from
different libraries combine unexpectedly. However, counter-arguments noted that this would force users to redeclare
members that already exist (like `Length`), potentially causing ambiguity errors when both namespaces are imported. Those
extensions may not even be ever usable, if the issue was that the underlying type did not provide a `Length`, for example,
then an extension being forced to provide a `Length` when it can never be invoked as an extension would be odd.

The LDM concluded that enforcing same-origin rules would add more complexity than it prevents. If the type already has a
`Length` property that wasn't intended to participate in patterns, that's unfortunate, but preventing extensions from
filling in the rest of the pattern only works if the member happens to be named `Length`; otherwise an extension could
add it anyway. Analyzer rules could flag suspicious combinations if this becomes a real problem in practice.

A follow-up question was whether extension members should also contribute to *implicit* indexers (the fallback that
uses `Length` + `int` indexer instead of direct `Index`/`Range` indexers). The same maximalist reasoning applied: if
you add an extension `int` indexer, you shouldn't need to also add an `Index` indexer just to use `c[^1]`.

##### Conclusion

We will allow permissive extension applicability. Extension members can contribute to pattern-based indexer matching
from multiple sources.

#### Open question: extension indexer access ordering

Open question: https://github.com/dotnet/csharplang/blob/7a58785d96afc903219cb33f45360744211c3c4c/proposals/extension-indexers.md#confirm-whether-extension-indexer-access-comes-before-or-after-implicit-indexers

The final open question concerned lookup ordering. When binding an element access, the current order is:

1. Instance indexer (`Index`/`Range` signature)
2. Instance implicit indexer (`Length` + `int`/`Slice`)
3. Extension indexer

The previous decision to allow extensions to contribute to implicit indexers opened a can of worms about priority. What
happens when an inner extension scope defines `Length`, an outer scope defines an `int` indexer, and an even outer scope
defines an `Index` indexer? Different orderings lead to different behavior.

Several approaches were discussed:

1. Process instance members fully (both direct and implicit indexers) before any extensions
2. At each extension scope, try direct indexers then implicit indexers, then move to the next scope
3. Prioritize direct `Index`/`Range` indexers everywhere, then fall back to implicit patterns

There was agreement that instance members should be fully exhausted before looking at extensions, but the exact ordering
when multiple extension scopes are involved remained unclear. We think this requires more investigation with concrete
examples to understand all the consequences. Depending on how we do searches, and for what piece of the language, there
could be very odd priority inversions and member selections that are not obvious to readers. For example, if there are 3
scopes in order from `Length`, `Index`-based indexer, and `int`-based indexer, what is the expectation on what is being
called for `underlying[^1]`?

##### Conclusion

We need to revisit this question with a proposal that works through the combinations systematically. The extensions
working group will investigate and bring back a more concrete recommendation.
