3.8 KiB
C# Language Design Notes for April 6th, 2020
Agenda
Init-only members
Discussion
We have a proposal to dive into: https://github.com/jaredpar/csharplang/blob/init/proposals/init.md
-
The proposal notes that you can set
init
fields of the base type during construction, similar toreadonly
. This is not howreadonly
works today, only the declaring type can set readonly fields -
The proposal allows
init
on class and struct declarations as a shorthand forinit
on types. This is different from howreadonly
struct works today, where there is no syntactic shorthand,readonly
simply adds the additional requirement that all instance fields are markedreadonly
. -
For the runtime: does this feature prohibit runtime restrictions on setting
readonly
instance fields in the future? Put simply: yes. To avoid breaking C#, the runtime would be required to either respect the proposedInitOnlyAttribute
, or restrict optimizations to not alter the code for these features. -
Use in interfaces: the proposal prohibits it, but the following example seems useful:
interface I { int Prop { get; init set; } } public void MyMethod<T>() where T : I, new() { var t = new T() { Prop = 1 }; }
-
Signature compatibility: should
init
properties be compatible with mutable ones? That is, should removinginit
in favor of a bareset
be binary-compatible? This impacts our decisions for how we think about safety in older compilers:-
If we use a modreq to prevent other compilers from unsafely using a
setter
, that affects the signature of the method, and would make the above a breaking change -
If we want accept that older compilers are not a problem (C#, VB, and F# will all be updated), perhaps we don't need to specially guard this at all
-
We could use attributes to mark and guard, by using the
Obsolete
attribute.ref struct
s use this guard by having an Obsolete attribute with a reserved message, that is ignored by compatible compilers.
-
Accessors
Should we allow three accessors: get, set, init
? A big problem here is that you can end up
calling instance members in a constructor that invoke the setter, not the initter, for a
property. This means that there are few, if any, invariants that hold for init vs. set, and
weakens the feature significantly.
Syntax
init
vs. initonly
for syntax. On the one hand, init
is inconsistent with readonly
(vs. initonly
), but on the other hand we're pretty sad that readonly
is such a long keyword. It also has few analogies
with properties, where readonly
isn't allowed at all, and the shorter init
keyword seems more similar to
get
and set
-
Usage of
init
as a modifier: there are a number of different meanings here, and similarities betweenreadonly
andinit
is separating the more places we use it. For instance, ifinit
is allowed as a member modifier, it seems similar toreadonly
on members, but they actually do different things. Moreover,readonly
on types means that all instance fields and methods arereadonly
, whileinit
on types would only meaninit
on fields, not on methods. -
What are the uses for
init
methods, aside from helper methods? Could they be used in collection initializers?
Required Initialization
The proposal in this doc is that required initialization is also an important feature for setters. We're
going to leave that discussion for a future meeting. As proposed, nullable warnings are not modified for
init
members.
Conclusions
No init
on types. No agreement on syntax. We probably have to talk about more of the use cases.
We've decided that having three different accessors is not helpful. Settled that we will place a
modreq
on all init
setters.