4.1 KiB
C# Language Design Meeting for March 23rd, 2022
Agenda
Quote of the Day
- "We can and do allow users to shoot their feet off. However, lets not give them something capable of doing nothing else"
Discussion
Open questions in required members
https://github.com/dotnet/csharplang/issues/3630
https://github.com/dotnet/csharplang/issues/5945
Emitting SetsRequiredMembers
for record copy constructors
We're not ok with option 1 here: these constructors can be accessed directly by consumers within the record type or any derived type of the record. We also think that option 2
changes too much metadata to be palatable: every existing record would change on recompile. While incorrect compilation order could result in a SetsRequiredMembers
being missed
from a derived record, we think that this case is sufficiently in the corner that it's not necessary to try and address.
Conclusion
Option 3, SetsRequiredMembers
will be added to record copy constructors when the record or any base type has required members.
Should SetsRequiredMembers
suppress errors?
This is a corner case, as the scenario cannot arise from C# code, but it could potentially occur if another language were to extend a type with required members and either
override (without applying the correct attributes) or hide those members. This would cause the required member list to not be understood by the language, and there is danger in
allowing something we don't understand to proceed. However, in those cases, the external language user did decide to explicitly add the SetsRequiredMembers
attribute: this is
a signal we can understand without context, so it seems fine to ignore errors around required members if the attribute is found.
Conclusion
SetsRequiredMembers
will suppress the error.
Unsettable members
We've previously said constructors that require members must be at least as accessible as the members, to ensure that constructors don't become uncallable. This is just an extension of that scenario.
Conclusion
We will require that required fields cannot be readonly, and that required properties are settable from every constructor (meaning that they have a setter/initer at least as accessible as every constructor that requires them to be set).
Ref returning properties
There's an interesting debate here similar to the placement of readonly
in comparison to the ref: does required
here require setting of the ref itself (not possible with
ref properties, might be possible with ref field?), or does it require setting the value pointed at by the ref? If the former, that would suggest required ref
to be in line
with readonly
members, and would necessitate a syntax for initializing a ref in an object initializer, which doesn't exist today. If the latter, it would suggest
ref required
, to be in line with ref readonly
, but it's unclear if this is a scenario to be concerned with.
Conclusion
Given lack of motivation, this is disallowed entirely (on both ref properties and, when added, ref fields). We can revisit in the future if we have motivating use cases.
Obsolete members
Finally, we considered Obsolete
required members. We're not fans of libraries forcing their users into a warning, either by accident (adding Obsolete
to a member while
forgetting to remove required
) or intentionally. However, we don't feel this is worth an error: it's technically understandable, we just don't know why someone would intentionally
do it. This category of issue is best served by warnings, so we think that's the appropriate strategy.
Conclusion
Option 3, with a warning. Warnings will be suppressed for Obsolete
constructors and for types that are obsolete.