- Betterness for collection expressions and span types
- Type inference from collection expression elements
- Collection expression conversions
Overload resolution will generally consider a more "specific" overload better. Normally "specificity" is expressed through the existence of an implicit conversion from the more to the less specific, but occasionally we need another measure of specificity in the betterness rule. One previous example of this is interpolated string handlers.
The proposal is to explicitly declare the span types Span<T>
and ReadOnlySpan<T>
more specific - "better" - than arrays and the core collection interfaces when targeted by a collection expression, even though an implicit conversion does not exist.
The motivation is that picking a span overload is likely to be more efficient, since the span created from the collection expression may potentially be allocated on the stack instead of the heap.
One question is whether this should apply only to the "official" span types, or whether it should generalize to all ref structs that satisfy the requirements for being created by a collection expression? After all, it is not unreasonable to expect that other people have ref struct collection types of their own.
Also, should this apply only when compared to arrays and interfaces, as currently formulated, or also against concrete (class or struct) types like List<T>
? Would an ambiguity error better match expectations?
Yes to the overall proposal: Span types should be better. The working group will come back with recommendations for the open questions.
The proposal allows type inference to recursively inspect the element expressions of a collection expression, similar to tuples and lambdas. This allows for better inference results, and fewer meaningful cases that lead to errors.
It gracefully handles empty collections, and "coordinates" better with other parameters.
Adopted!
There are five ways in which types can qualify for having a conversion from collection expression. For four of them, there must be an implicit conversion from each element expression, and from the iteration type of each spread element, to the iteration type of the target type.
For the fallback case of collection initializer types, however, the iteration type is not used, because the expression is instead mapped to individual Add
calls, and each could resolve to a different overload.
(Are there situations where a collection expression conversion should be considered an identity conversion in order to be allowed as an in
argument? The working group will explore this question.)
This does not support "legacy" collection initializer scenarios where Add
methods take more than one value. This is mostly used for dictionaries today, and when we start supporting dictionaries we would include those scenarios.
Should the list of core collection interfaces for which conversions a supported be given as an explicit list, or simply "the generic interfaces implemented by List<T>
"?
It should be an explicit list. This should not "automatically" grow in the future if List<T>
implements new interfaces, but each should be a result of a deliberate decision.
Should conversions to Memory<T>
and ReadOnlyMemory<T>
be supported?
This doesn't fall out automatically. The working group should explore this further.
Should conversions to inline arrays be supported?
Yes, but it is low priority to expose in this release. Efficient implementation of interface target types will probably use inline arrays under the covers regardless, so this might not be all that expensive to implement.