Back to Csharplang

C# Language Design Meeting for March 17th, 2025

meetings/2025/LDM-2025-03-17.md

latest6.7 KB
Original Source

C# Language Design Meeting for March 17th, 2025

Agenda

Quote of the Day

  • "I hope no one was confused by me saying coversion instead of conversion" "Contraversion"

Discussion

Collection expression arguments

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

with breaking change

Question: https://github.com/dotnet/csharplang/blob/d139d200cfe8ffe45536cdcc8b17ee7f25b5757b/proposals/collection-expression-arguments.md#with-breaking-change

We started today by looking at whether, and if so where, to take a breaking change around the meaning of with() in a collection expression. We don't want [with(1), with(2)] to have different meanings for what with binds to, so we're ok with a breaking change in general. However, we haven't yet had the in-depth debate on whether with is the final syntax; given this, we don't want to risk anything on lower language versions at this time.

Conclusion

We will take the breaking change around with inside a collection expression, but only in language version preview for now.

Collection expression arguments and conversions

Question: https://github.com/dotnet/csharplang/blob/753119dee9eb5e5a4bd36072559b25d9064c63e6/proposals/collection-expression-arguments.md#should-arguments-affect-collection-expression-conversion

We think that the precedent around new() is good here. It's possible that we could relax the language here in the future, but we think that if we do so, it should be done holistically, for both new() and collection expressions, rather than just for collection expressions. new() doesn't do this because of the concern that it would mean adding a new constructor to a type could become a source-breaking change, and we think that we'd have to delve into that concern for both constructs at the same time.

Conclusion

Collection expression arguments will not be used as part of determining whether a collection expression conversion exists.

Dictionary expressions

Champion issue: https://github.com/dotnet/csharplang/issues/8659
Specification: https://github.com/dotnet/csharplang/blob/main/proposals/dictionary-expressions.md Question: https://github.com/dotnet/csharplang/blob/d139d200cfe8ffe45536cdcc8b17ee7f25b5757b/proposals/dictionary-expressions.md#conversion-from-expression-element-for-keyvaluepairk-v-collections

Following up from last time, the working group brought a proposal around key/value pair conversions in dictionary expressions. We're a bit concerned about the broad applicability of the proposed rules: they block off user-defined conversions when the iteration type is a KVP, even if we're not converting to a dictionary expression. That seems wrong to us; the user indicated how to convert a given type to a KVP, so why would we ignore that information? Our desire for covariance in KVP does make this more complicated than a standard conversion though. For example, what if the type specifies 2 UDCs to different KVP instantiations? Which one would we prefer for a variance conversion to the iteration type? After some discussion, we think that such "multiple conversion path" types are a sufficiently small corner of a corner that we don't have to worry about it: either better conversion will return an exact conversion to the element type of the collection expression, or we'll get an ambiguity.

We also briefly re-examined whether we actually want to support these variance scenarios outside of pure dictionary types, and our conclusion is that we do. There are thousands of examples of IEnumerable<KeyValuePair> APIs on GitHub, for methods that take any form of dictionary or list of KVPs, and we would like to support simple syntax for such APIs.

Conclusion

We will allow all standard element type conversions that we allow in normal collection expressions when the element type is a KVP. We will allow KVP covariance in all collection expressions.

Extensions

Champion issue: https://github.com/dotnet/csharplang/issues/8697
Specification: https://github.com/dotnet/csharplang/blob/b484275c4369638c2c5f9305716582c76a1c9335/proposals/extensions.md

Accessibility

Question: https://github.com/dotnet/csharplang/blob/b484275c4369638c2c5f9305716582c76a1c9335/proposals/extensions.md#accessibility

First up today, we continued from a previous LDM on the topic of accessibility. We think our initial gut feeling was correct; extension blocks are not real items. You can declare multiple extension blocks for a single type, would private be private just to a single block, or to all shared blocks? There are also other decisions that we'd need to revisit if we started giving blocks a stronger identity here.

Conclusion

Accessibility in extension blocks is relative to the containing static class.

Static factory scenarios

Question: https://github.com/dotnet/csharplang/blob/b484275c4369638c2c5f9305716582c76a1c9335/proposals/extensions.md#static-factory-scenario

The working group took a look at the previous decision around static method overloading, and has come to the conclusion that there are current technical limitations around modopt/modreqs in the compiler that would make using them to permit overloading here difficult; today, Roslyn does not support using anything but a named type in a modopt or modreq. This is a compiler restriction, not a runtime restriction, but would be a somewhat involved change to fix up. However, we're also questioning whether we should support this type of overloading at all in extensions: sure, we can figure out how to emit such methods, but what would the disambiguation syntax be? We've already walked back from adding new syntax in other places for this, and while we do think that the scenario where a user wants to define the same static method on several types isn't unreasonable, we also think that we might be able to start restrictive for a preview period, and then listen for feedback.

Conclusion

We will enforce standard overloading rules across extension blocks, so static methods on different underlying types in the same static class will not be able to share the same name and parameter types.