Skip to content

Latest commit

 

History

History
86 lines (58 loc) · 4.37 KB

File metadata and controls

86 lines (58 loc) · 4.37 KB

Collection literals working group meeting for April 28, 2023

Agenda

  • Should a collection literal have a natural type? If so, what type?
  • Infer the collection type from spread elements?
  • Should dictionaries use overwrite or add semantics?

Discussion

Should a collection literal have a natural type? If so, what type?

Without a natural type, a collection literal can only be used when target typed to a constructible collection type.

Given the best common type T of the elements, the choices for natural type include:

  • T[]
  • List<T>
  • Span<T>
  • ReadOnlySpan<T>
  • ImmutableArray<T>

We ruled out Span<T> and ReadOnlySpan<T> because ref struct types cannot be used in async contexts. Also, ref struct types cannot implement interfaces, so spans cannot be used as IEnumerable<T>.

Of the remaining types, List<T> is perhaps the most flexible and may satisfy more scenarios than T[] or ImmutableArray<T>.

Also, we speculate that List<T> is commonly used today in method bodies, so having List<T> as the natural type would be familiar and would align with existing code.

Another consideration is consistency between the natural types for non-dictionary and dictionary collection literals. The most obvious choice for dictionaries is Dictionary<TKey, TValue> which is consistent with List<T>.

However, different collection types are appropriate for different scenarios.

If List<T> is used as the natural type for a particular variable, but Span<T> or T[] could have been used explicitly instead, the choice of List<T> may be more expensive.

List<T> is mutable, even though it may be uncommon to mutate a collection that was created with an explicit initializer. That means consumers that want immutability will need to use an explicit type or wrap the instance.

The alternative is no natural type.

Collection literals are not a simple replacement for existing collection construction. Previously, constructing and populating a collection required handling details of capacity, allocations, indexing vs Add() vs AddRange(), etc., and using List<T> may have been simpler or more familiar than alternative collection types. Collection literals address that by providing a common syntax that can be used for a range of collection types. Consumers can now choose the optimal collection type for each scenario, and requiring the developer to specify the collection type explicitly helps that.

Some other alternatives:

  • Add support for natural type later.
  • Support natural type behind a feature flag.
  • Support natural type now and also include an analyzer that warns for var with collection literals for performance reasons.
  • Support var element types, as suggested in a recent LDM: List<var>, var[], Dictionary<var, var>, etc.

Conclusion

No conclusion, although the most likely options for natural type may be List<T> or no natural type.

Infer the collection type from spread elements?

Should the collection type of a collection literal be inferred from the collection types of any spread elements?

ImmutableArray<int> a = [1, 2, 3];
var b = [..a, 4]; // ImmutableArray<int> b

What if spread elements have distinct types, such as HashSet<object> and HashSet<string>, or distinct comparers?

What if the spread element type is an interface, such as IEnumerable<T> or IReadOnlyDictionary<TKey, TValue>, or not a constructible collection type?

There is the potential for breaking changes to var collections when the types of spread elements change:

  • between an interface and a concrete collection type, or
  • between a constructible and non-constructible collection, by adding or removing a public constructor.

Conclusion

Requires further discussion.

Should dictionaries use overwrite or add semantics?

Should construction of a dictionary from a collection with duplicates use overwrite or add (and throw) semantics?

There are valid scenarios for both. The concern is when combining collections where the collection types use distinct semantics.

There is a similar issue around comparers. Consider the non-dictionary case when combining two HashSet<T> instances that use different comparers. There is potential for data loss in the resulting collection.

Conclusion

Requires further discussion.