Back to Csharplang

C# Language Design Meeting for May 12th, 2025

meetings/2025/LDM-2025-05-12.md

latest5.8 KB
Original Source

C# Language Design Meeting for May 12th, 2025

Agenda

Quote of the Day

  • "Why would you say that?" "Look, hubris is my thing" [much later] "Hey, I didn't jinx it after all!"

Discussion

Collection expression arguments

Champion issue: https://github.com/dotnet/csharplang/issues/8887
Spec: https://github.com/dotnet/csharplang/blob/efb435c829ca6917dbdd34ca30eab4a47ff66c22/proposals/collection-expression-arguments.md

Empty argument lists

Question: https://github.com/dotnet/csharplang/blob/efb435c829ca6917dbdd34ca30eab4a47ff66c22/proposals/collection-expression-arguments.md#empty-argument-lists

First up, we looked at when to permit empty argument lists. We want a simple rule here, something easy to explain to users. One option is just "allowed for everything", which is certainly easy to explain. However, it may run into friction in the spec and compiler; what does it actually mean, particularly for arrays and spans? Neither of those have a traditional "constructor" that can accept arguments, so what does the with() actually bind to? Another simple option is to permit it when there exists a constructor or Create method that can accept the empty argument list. For such a rule, the main question is then whether interfaces can accept an empty argument list. We think this might be slightly more common than for arrays or spans; it is reasonable for an API author to move from a specific type to an interface to abstract things in a breaking update or internally. It would mean that we would need to amend the list of accepted constructors for interface types to include the empty constructor as well, but that seems reasonable. A final option would be to simply disallow an empty with() entirely, but that feels too heavy-handed; being explicitly about not passing arguments is a perfectly reasonable thing to do. Therefore, we will allow with() for constructor types and builder types that can be called without arguments at all, and we will add empty constructor signatures for the interface types.

Conclusion

We will allow with() for constructor types and builder types that can be called without arguments at all, and we will add empty constructor signatures for the interface types. Arrays and spans will not allow with(), as there are no signatures that would fit them.

Constructor binding behavior

Question: https://github.com/dotnet/csharplang/blob/efb435c829ca6917dbdd34ca30eab4a47ff66c22/proposals/collection-expression-arguments.md#collectionbuilderattribute-methods

Following up from last week, we're revisiting the rules for create method binding. We have a new proposal that regularizes the approach by creating projection signatures: chopping off the last parameter of the builder method, then doing standard overload resolution rules, creating a signature that is similar in approach to a constructor signature for types that use constructor-based creation. This does nicely regularize the approach, and solves the questions around which ROS to use if multiple are in a signature, but does still present the question of what do users do to use optional parameters? After some discussion, we've hit on the mental model that users are likely to not know exactly what they're calling: a Create method, a constructor, a synthetic constructor for an interface, they all appear the same from a mental model perspective. The projection also fits in with that model, and that's what we intend to proceed with.

Conclusion

Projection-based approach is approved.

Syntax

Also last week, we had 2 syntax choices to finish on: whether to use ; or , as the separator for collection arguments, and whether to use with or args as the keyword. We did very little discussion today, and instead went straight into a read of the room. This revealed that, while there is still some support for both ; and init, both are in the minority. We will approve with(), as the syntax for collection expression arguments.

Conclusion

with is the keyword chosen for collection expression arguments, and , is the separator.

Dictionary expressions

Champion issue: https://github.com/dotnet/csharplang/issues/8659
Spec: https://github.com/dotnet/csharplang/blob/efb435c829ca6917dbdd34ca30eab4a47ff66c22/proposals/dictionary-expressions.md#support-keyvaluepair-variance-with-params

Finally today, we took a look at a question in dictionary expressions: should we support KVP variance in params. This pulls in two directions: as a bare parameter, KVP has no variance. However, if the user were to surround the provided params argument with a collection expression, it would start to work. We've further said in the past that we want to try and minimize the differences between what collection expressions can do, and what params can do.

That being said, there are also good arguments for disallowing this. The general idea behind params is that the user doesn't know that they're calling something that accepts a collection, and they get the same experience they'd get if they called a method that just had a signature with however many parameters they provided. There are also other aspects of collections expressions and params that differ, such as extension GetEnumerator. We also aren't adding generalized KVP variance; it's specifically a feature of collection expressions. We therefore think we should keep it limited to just collection expressions.

Conclusion

Variance is limited to explicit collection expressions, params does not perform it.