- "It's our best chance of having a stem cell"
Continuing to follow up on open questions, we turned our attention to
validating InlineArrayAttribute
applications and whether nested object initializers can use indexers on inline
arrays. For the former, we're unanimously in favor of the compiler validating that the attribute was applied correctly.
The latter proved to be a more interesting conversation; we brought up that arrays are supported, but this actually
appears to be a spec violation of the C# compiler, as the syntax is supposed to require an indexer member to be defined
on the type being initialized while arrays do not define any such member. We also don't think there's any real demand for
this to be natively supported, and by leaving it out users can opt into the feature by defining their own indexer on
their inline array types. This allows us to have our cake and eat it too: no implementation complexity for us, and users
can get the syntax they choose if they want it.
We will validate InlineArrayAttribute
applications. We will not natively support inline arrays for object initializer
index initialization, but we won't block it if the user defines their own indexer either.
We considered a couple of open questions in primary ctors.
We're concerned about allowing attributes on captured parameters. From a language perspective, these are not fields,
and don't have to be captured in fields. This would not be the only thing that a user might want to put on their
captures; for example, there has been significant feedback that users would like their captures to be readonly
.
This doesn't seem more important than that scenario, so why would this work while that does not? It seems like, if we
ever get syntax for specifying more about how the parameter is captured, it would be appropriate to have this feature
at that point, but until then we would like to hold off.
Not allowed.
One piece of feedback that we've seen is that users are concerned about shadowing members from base types accidentally. While this is concerning, we're also concerned that a broad warning might impact a common scenario where the parameter is passed to the base to initialize the shadowing member. We need to think more about this conflict, so we'll do so and come back to the problem at a later meeting.
No conclusion today.
#5354
https://github.com/dotnet/csharplang/blob/c8c1615fcad4ca016a97b7260ad497aad53ebc78/meetings/working-groups/collection-literals/CL-2023-04-28.md#should-a-collection-literal-have-a-natural-type-if-so-what-type
Finally today, we considered whether collection literals should have a natural type. There are a couple of slightly
different versions of this scenario: var
, and IEnumerable<>
, but var
is what we spent most of the time discussing.
In many ways, a natural type for a collection is going to be surprising, no matter what we choose. Some users will
expect such collections to behave like other C# literals: immutable and cacheable. Another segment might expect the
literal to be typed as an array: not immutable, but not growable either. A third would expect such literals to be a
type that can be added to later, such as List<>
. No matter what we choose, if we choose a natural type, two of the
segments are going to be surprised by the decision.
So what if we didn't have a natural type at all? Today, users always need to think about the type of a collection when
it is created. Either it needs to be assigned to a local or parameter that has a type, or it's being created by some
factory method or constructor that has to be typed. One of the goals of the proposal to simplify this, and that requires
having a natural type. But it does feel odd that, for a proposal that goes so far to be optimal when we know the
target type, it cannot be optimal in the natural type case. For the cohort of users that want these to be immutable,
any mutability in the natural type means that the literals cannot be cached, resulting in lots of extra allocations. For
those that expect an array, using List<>
or some other similar type means that extra overhead is incurred, and trivial
cases like empty literals cannot be shared. For users who expect List<>
, either of the other two means that they will
have to specify the collection type at creation, making it no longer a simple scenario. Unfortunately, not having a
target type at the start and then adding one later is potentially a breaking change. We did it for lambdas, but it caused
significant pain for implementation and has a number of caveats, so if we can avoid the same gymnastics that would be
helpful.
We also thought a bit about a middle ground, where a collection literal only has a natural type when it is observably
stored in a location. For example, when iterated over in a foreach
, a collection literal could be emitted however is
most efficient for the compiler. This does then introduce another risk of deoptimizing by extracting variables. This can
already happen with interpolated string handlers, but that is fairly rare. This might be similarly rare, but more such
cases always need to be approached with caution.
We have no conclusions here this week. We'll come back and think about it more in a future LDM.