- Proposed
- Prototype: Complete
- Implementation: In Progress
- Specification: Not Started
We allow parameters to be marked with readonly
. This disallows them from being assigned to or being passed by ref
or out
.
C# users have long requested the ability to mark both locals and parameters as readonly
. The design team has somewhat resisted this for two reasons:
- The view that
readonly
on parameters and locals would be an attractive nuisance more than a helpful addition. - Indecision on what a succinct syntax for locals would be to minimize the nuisance part of the first objection.
However, the addition of primary constructor parameters changes that calculus, at least for parameters. A significant piece of feedback from the initial preview is that users would like to be able to
ensure that primary constructor parameters are not modified. The scope of such parameters is much larger, so the danger of accidental modification is much higher. We therefore propose allowing readonly
as a parameter modifier.
This proposal makes a few assumptions about readonly
locals as part of it:
- A future design would allow
readonly
as a local modifier. - That future design might allow a shorthand for
readonly var
, or may say thatreadonly var
is not allowed, and the separate shorthand is required for areadonly
type-inferred local. What that shorthand is (val
,let
,const
, or some other keyword) is beyond the scope of this proposal.
These assumptions allow us to presume that to fully spell out readonlyness for a parameter or local requires a modifier, and that modifier is readonly
. In places where types can be inferred, we offer a
shorthand that combines the meanings, but otherwise readonly
is required.
We modify section 15.6.2.1 of the C# specification with the following new definition for parameter_modifier
:
parameter_modifier
: parameter_mode_modifier
| 'this'
| 'readonly'
;
The spec is not yet updated to include the draft C# 12 specification for ref readonly
parameters, but we will restrict
readonly
such that if a parameter is both readonly
and ref readonly
, the readonly
must appear on the left side of the ref readonly
, so that readonly ref readonly
is permitted, but ref readonly readonly
is not.
For a readonly
parameter, the compiler will issue an error when it is assigned to or taken as a mutable lvalue
. This means that a readonly
parameter cannot be passed by out
or ref
, but can be passed
by value, ref readonly
, or in
.
For partial
methods, we allow the implementing partial
method declaration to add the readonly
modifier to a parameter if the defining partial
method did not. If the defining partial method
included the
readonly
modifier, the implementing partial
method must also include it.
abstract
members, interface
members, delegate types, and function pointer types are not permitted to specify readonly
on their parameters
Overriding members are not required to match the readonly
ness of overridden member's parameters. readonly
may be added or removed with no effect on the program.
Unresolved question
The presence of readonly
on a parameter has no impact to the generated code. It is not possible to determine from metadata whether a parameter is readonly
or not.
Earlier the proposal alluded to this feature being an attractive nuisance. To spell it out more clearly, we are worried that by introducing a verbose modifier that many people would like to be the default (including ourselves!), it will become the new "thing to do" on every method definition, even in cases when it provides no real safety benefits.
The championed issue has a number of alternative designs, but most center around the axis of: should we introduce a new, shorter modifier, or
a shorthand that can apply to both locals and parameters? For example, val int i
as a parameter definition would be what this proposal calls readonly int i
. This shorthand is very inconsistent with standard C# behavior, so
this proposal takes the position that we would only want to introduce a shorthand for the readonly
+ type inference case.
This general feature has historically been pushed back on due to the attractive nuisanceness of the feature. We could artificially restrict this to just primary constructor parameters to avoid introducing that in general.
Today, when applying a modifier to a lambda parameter, the type must also be spelled out. For example, this is not permitted:
delegate void D(ref int i);
D d = (ref i) => {};
The LDM has long thought about allowing the type here to be omitted, but has not yet done so. Should we make that change as part of this proposal? Or should we say that readonly
, like ref
, means that the type of the lambda
parameter must be spelled out?
This proposal states that readonly
on parameters has no effect on the emitted code, and that it will not be possible to tell from metadata (including things that read metadata, such as reflection) that a parameter was declared as
readonly
. Are there use cases for reflecting this information in metadata, and if so, what should the emit strategy we use to convey this information be? And, if we do emit to metadata, should that change how overriding
carries through that information?