# C# Language Design Meeting for September 27th, 2023

## Agenda

- [Collection expressions](#collection-expressions)

## Quote of the Day

- "Note today's date. Today is a historical day when we finish collection expressions" *collective laughter*

## Discussion

### Collection expressions

https://github.com/dotnet/csharplang/issues/5354  
https://github.com/dotnet/csharplang/issues/7542

Today in our wrapping up of the last issues in collection expressions, we took a look at what optimizations the compiler is allowed to make. There's a tension here
between allowing the compiler to make user code fast, and how understandable the feature will be. There are a number of features that we want the compiler to be able
to take advantage of that might not normally be visible, or not be chosen, at the location of the collection expression. For example, .NET 8 is introducing a new set
of `AddRange` extension methods on `List<T>` for adding a `ReadOnlySpan` to a `List` efficiently. We'd like to pick these methods for spreading an array into a collection
expression target-typed to a `List`, but because `AddRange` is an extension method, it's never even discovered before the instance `AddRange(IEnumerable)` method is picked.
This is not just a problem for collection expressions, of course; it's a problem for regular invocations as well. But do we want to limit collection expressions to
behaving exactly as if the user had written the `AddRange` call themselves, or can we make it better? And if we can make it better, where do we limit ourselves? For
user-defined collection types, should we search non-imported namespaces for better extension `AddRange` methods? If so, where do we stop? And how do API authors know how
to design their collections to take advantage of these rules?

One thing we wanted to do as part of determining what APIs can be taken advantage of is to determine what our definition for the Base Class Library, or BCL, actually is.
With input from the runtime team, we determined that what we should consider the BCL is anything in the `System.*` namespace. With that definition, we decided to split up
our decisions: should we be allowed to assume well-behavedness and call any method at all for BCL types? And, as a separate question, should we be allowed to do that for
non-BCL types? For BCL types, we think this is fairly safe. The `System` namespace has been "special" to the .NET ecosystem since the very beginning, and we feel ok with
assuming that we can call things like the extension `AddRange` for BCL types in collection expression spreads. We're also ok with calling things like `CopyTo` on BCL types.
We're more wary about non-BCL types, particularly around type author discoverability. How do we make sure that type authors know what their collections should do to get
optimal performance, for example, or how do we know that a random `AddRange` method would actually do what we expect for specific BCL types like `List`? After much debate,
we think we need to be more specific for non-BCL types, provide a specific list of things that we will call, and respect the normal rules that users will see if they write
out the code manually.

Separately, we would like to address some of the problems that caused `AddRange` to be an extension in the first place. Because arrays and `string`s are both convertible
to `ReadOnlySpan<T>` and implement `IEnumerable<T>`, adding that `AddRange` as an instance method would create ambiguities when passing a parameter of that type to `AddRange`.
The array problem can be fixed with another more specific overload, but the `string` case cannot, as it would require generic specialization. This is a problem that we
keep running into with various language features, and we think it's time to address it in C# 13.

#### Conclusion

The compiler can rely on all implementation details of BCL types, and potentially call methods that would not normally be preferred by the language in that location. For
user types, we need to enumerate the list of methods that the compiler can call, and respect the normal language rules in those locations. This include methods from well-known
BCL interfaces implemented by non-BCL types. We will look at potential ways to unify this behavior in C# 13.
