meetings/2026/LDM-2026-03-09.md
Champion issue: https://github.com/dotnet/csharplang/issues/9856
Spec: https://github.com/dotnet/csharplang/blob/ee06dd0eaaff1ddb16d0f6680107a1a00aecee92/proposals/extension-indexers.md
We continued discussion of the remaining open questions around extension indexers and their interactions with list patterns. The
core tension is that multiple lookup principles all seem desirable in isolation: instance members should continue to beat extensions
for compatibility, inner extension scopes should generally beat outer scopes, and real this[Index]/this[Range] members are
usually preferable to implicit indexers assembled from Length or Count together with this[int] or Slice. Unfortunately,
these preferences conflict once extension members are allowed to contribute to indexing.
The hardest question was how to order extension lookup once implicit indexers are involved. We compared two broad approaches. One would continue to prioritize inner extension scopes over outer scopes, even when that meant selecting an implicit indexer assembled from closer members instead of a real indexer found farther out. The other would keep the strong preference that instance members always beat extensions, but once extension lookup was required it would give a weak priority to real indexers over implicit ones, even if the real indexer came from an outer extension scope.
We chose the second ordering. We feel that a real indexer remains a more coherent single declaration than an implicit indexer assembled from multiple members, and that this weak preference was worth preserving once the search had already moved beyond instance members. This does make extension lookup somewhat irregular, because the language will no longer uniformly prefer the closest extension-provided solution in every case, but the LDM preferred that irregularity over a rule that would make real extension indexers lose to extension-based implicit combinations more often.
List patterns were discussed alongside ordinary indexer access. We do not want list patterns to use one Length or
Count member for shape checking and then silently use a different one when resolving indexing behavior. Instead, list pattern
support should be described in terms of independently resolving the members needed for the operation, using the same overall
ordering principles as indexer access.
We workshopped the following specese during the meeting, which will need to be refined before inclusion in the speclet:
- List patterns are resolved as if we look for Length/Count, Index indexer and Range indexer individually
- For Index and Range indexers, proceed as follows: a. With instance lookup only, find the "real" index if possible b. With instance lookup only, find the parts of the implicit indexer if possible c. With full lookup (instance+extension), find the "real" index if possible d. With full lookup (instance+extension), find the parts of the implicit indexer if possible (each in individual lookups)
When resolving extension-based indexing behavior, instance lookup remains strictly better than extension lookup. After that,
the language will give a weak preference to real Index and Range indexers over implicit indexers, even when that means an
outer extension scope can beat an implicit solution formed from a closer scope. List patterns will resolve the required
members using the same overall ordering model rather than treating implicit indexers in extensions as unsupported.
We next looked at the Slice portion of implicit range support. The question was whether, once extension members are
allowed to contribute, only extension-block Slice members should count, or whether classic extension methods named Slice
should count as well. Historically, neither form contributed here, so enabling extension participation would cause some code
to start compiling where it previously produced an error.
We see no good reason to distinguish the two forms. The extensions feature has consistently tried to make classic extension members and extension-block members interchangeable from the user's point of view, and this scenario should follow that same principle. Moving from an error to a successful binding was also considered acceptable here, because it does not break previously-working code and is a natural consequence of allowing extensions to participate in range access.
Both classic extension Slice methods and extension-block Slice members will contribute to implicit range access under the
new rules.
Finally, we considered whether extension Length or Count properties should participate in the optimization for spread
elements in collection expressions. That optimization exists to avoid unnecessary reallocations by pre-sizing the destination
when the compiler can cheaply determine the number of elements that will be produced.
The concern was that an extension Length or Count member is much less likely to represent cheap, intrinsic knowledge about
the underlying type. In many cases, such an extension would have to enumerate the source to discover the count, which would
defeat the purpose of the optimization by adding an extra traversal before the actual spread occurs. We therefore
prefer to keep the optimization tied to information directly exposed by the source type itself. If compelling scenarios
emerge later, or if there is a better hook for efficient optional counts, the language can revisit this decision.
Extension Length and Count members will not participate in spread optimization for collection expressions.