- "Can I add a 4th bullet? Don't design it now, do it now" "This isn't a Punnett square"
- "Ok, that was three deep breaths" "I only did 2" "I have small lungs, what can I say"
- posts a picture of the IEnumerable hierarchy "Thank you, , that was... horrifying. Horrifying and super useful"
Today, we looked at how the conversion for collection expressions is defined for IEnumerable
types. This is the rule that allows collection expressions to be fully
compatible with types that can be initialized by a collection initializer today; because of this broad compatibility, it has some issues that we think may cause the feature
to be brittle in the face of user interaction. Specifically, determining whether the conversion exists requires the compiler to attempt to bind the call to the constructor,
and every Add
or AddRange
call, before determining that a conversion exists. This can potentially lead to unstable behavior in the editor in the face of user errors, and
it's a problem we've seen before. In particular, interpolated string handlers were originally specified this way, but we revised the specification after considering this issue.
We are therefore considering updating the spec here to avoid the same problem. The new rules do have a marked disadvantage: they aren't fully compatible with all types that
can use a collection initializer types today. If there are extension Add
methods that convert inputs to the element type of the collection, they would not be considered by
this new rule. We do expect that these cases are somewhat rarer though, and that we can address this later if it becomes a problem.
These rules also prompted a discussion on support for non-generic IEnumerable
. The WG was unclear on the exact outcome from a
prior LDM, and whether the non-generic IEnumerable
and descendent interfaces (such as ICollection
)
need to be supported for target-typing as well. We clarified that we don't think we can make a decision about whether to consider the non-generic IEnumerable
"deprecated" by
ourselves; it would need to be a larger decision among all the .NET teams. We also don't think supporting the non-generic types will cause any new ambiguities that didn't already
exists: for example, if an API is overloaded on ICollection
vs ICollection<object>
today, it's already ambiguous, because ICollection<T>
doesn't inherit from ICollection
.
As part of this consideration, we also confirmed that we do want to be able to target the non-generic interfaces with collection expressions. IE, this should work:
ICollection c = ["a", 2, null];
However, we think we may not be able to get this in C# 12, and it may have to wait for C# 13 and be considered in tandem with natural types for collection expressions.
Finally, we thought about how the new rules apply to string
s. There are two core scenarios to think about here:
// Scenario 1
void M1(string s) {}
void M1(ReadOnlySpan<char> r) {}
M1(['a', 'b', 'c'])
// Scenario 2
void M2(string s);
void M2(char[] characters);
M(['a', 'b', 'c']);
Scenario 1 is extremely realistic, and in fact already exists in the BCL. We'd much prefer that it call the ReadOnlySpan
API, which does not need to allocate; the string
API
needs an instance of a string
, which could very well allocate if the items were not constant. To accomplish this, we need to update the betterness rule from a
previous LDM to consider Span
better than both array and string
, or it will become ambiguous.
The second API, though, is not ambiguous by today's rules, as string
is not constructable and therefore no conversion exists. By the new rules though, string
is a valid
target type for collection expressions, and will simply fail at a later stage. This makes the M2
API ambiguous. After some more consideration, we think that there's good
reasoning for making string
s constructable with collection expressions. List patterns already work on them, and we think that syntax could be useful for combining multiple
string slices together. Support for this, though, is likely not going to make C# 12, and will be considered for future C# versions.
The proposed change to the collection expression rules is adopted as worded.
Collection expressions can be converted to string
s, though the conversion will fail to compile today. We will hope to make it work in the future.
We update the overload resolution betterness rules to consider Span
and ReadOnlySpan
better than string
, just as we did for array and interfaces implemented by arrays.
We will support non-generic collection interfaces as target types, but possibly not until C# 13 in tandem with natural types for collection expressions.