# C# Language Design Meeting for April 20th, 2026

## Agenda

- [Closed hierarchies continued](#closed-hierarchies-continued)
    - [Generics and exhaustiveness](#generics-and-exhaustiveness)
    - [Accessibility and exhaustiveness](#accessibility-and-exhaustiveness)
    - [Interface convertibility of sealed classes](#interface-convertibility-of-sealed-classes)
    - [Concrete intermediate nodes](#concrete-intermediate-nodes)

## Quote of the Day

- "Oh, to be the most important person in the meeting"

## Discussion

### Closed hierarchies continued

Champion issue: https://github.com/dotnet/csharplang/issues/9499  
Spec: https://github.com/dotnet/csharplang/blob/03974b0deb3572569684aadcf146fe61a86fc09e/proposals/closed-hierarchies.md

We returned to closed hierarchies after implementation work uncovered a few remaining edge cases. The
common thread was how far we should go in adding declaration-time restrictions to preserve the
exhaustiveness story, versus accepting that some hierarchies can only be matched exhaustively in certain
contexts and reporting that fact at the point of use.

#### Generics and exhaustiveness

Spec section: https://github.com/dotnet/csharplang/blob/03974b0deb3572569684aadcf146fe61a86fc09e/proposals/closed-hierarchies.md#generics-and-exhaustiveness

We first revisited the interaction between `closed` hierarchies and generics. The current proposal already
has a type-parameter restriction intended to keep the set of relevant derived constructions finite for a
given closed base type. During implementation, however, it became clear that there are still scenarios
where a particular derived type can flow into a switch even though the programmer cannot write a useful
pattern for that type, because the relevant type arguments are only existentially known or depend on
constraints in a way that cannot be expressed.

We discussed whether that should force us to add still more restrictions to declarations. The room did
not think that effort would pay for itself. In these cases the compiler may understand that some subtype
is possible, but if the user cannot actually name that subtype then exhaustive matching by direct
subtypes is simply not available in that context. Rather than trying to outlaw every such hierarchy, we
preferred to rely on the exhaustiveness diagnostic at the use site and make that message more helpful
about why a default arm or base-type arm is still required.

##### Conclusion

We will not add a new restriction here. In these generic scenarios, users simply cannot always be
exhaustive by only providing subtype cases.

#### Accessibility and exhaustiveness

We then looked at a similar question through the lens of accessibility. If a `closed` base type is
visible in some context but one of its direct subtypes is not, should we reject that declaration up
front? There is an understandable desire to protect authors from publishing a hierarchy that appears
`closed` but cannot be exhaustively matched by all consumers.

At the same time, we do see legitimate scenarios where the author intentionally wants a public or
otherwise broadly visible base type with less-accessible concrete representations. In those cases the
benefits of `closed` are still real inside the implementation context, even if outside consumers cannot
name every subtype directly. Because of that, we did not want to impose a blanket rule that would forbid
useful modeling patterns. As with the generic case, if you cannot name all the relevant types in a given
context, then exhaustive matching with only subtypes just does not apply there.

##### Conclusion

We will not add any new restrictions related to accessibility.

#### Interface convertibility of sealed classes

Spec section: https://github.com/dotnet/csharplang/blob/03974b0deb3572569684aadcf146fe61a86fc09e/proposals/closed-hierarchies.md#interface-convertibility-of-sealed-classes

Another open point concerned whether closed hierarchies should follow the precedent C# already has for
conversions involving sealed types and interfaces. Today, if the compiler can statically determine that a
sealed type cannot participate in a given interface relationship, it rejects the conversion even though
that reasoning is not perfectly sound in the presence of COM. We are already comfortable with that
existing tradeoff.

We decided to extend that same precedent to closed hierarchies. If the compiler can prove from the closed
hierarchy that no inheritance relationship exists between the types in question, it should apply that
restriction consistently in both conversion directions, both to and from the interface. This keeps the
rules aligned with existing sealed-type behavior and avoids surprising asymmetries.

##### Conclusion

We will apply the same symmetric interface-conversion restriction used for sealed types to closed
hierarchies as well.

#### Concrete intermediate nodes

Finally, we revisited feedback from users with deeper hierarchies who want a type in the middle to be
both a concrete type they can instantiate and a `closed` parent for a further layer of specialization.
The appeal is that this avoids having to introduce an extra default leaf type solely to preserve the
current abstractness requirement.

We were not persuaded that this should change the design. Our guiding principle for `closed` is that it
is an exhaustiveness feature, not merely a way of blocking outside inheritance. If a `closed` type were
allowed to be concrete, then exhaustive switching would still need to mention the base type itself, which
turns that arm into an effective catch-all and loses much of the maintainability benefit. Keeping `closed`
implicitly abstract preserves the stronger exhaustiveness story and stays aligned with the other
restrictions we have already chosen for the feature.

##### Conclusion

We will continue with the current design. A `closed` type remains implicitly abstract, and users who want
both an intermediate node and a default concrete case can model that with a separate type.
