3.4 KiB
C# Language Design Meeting for April 20, 2020
Agenda
Records:
- Factory methods
Discussion
The proposal at its core is to allow certain methods to opt-in to certain language semantics
that are only currently valid at construction, namely object initializers, collection
initializers, and the new with
expression (although that expression is legal on only certain
factory methods).
Possible extension of the feature: allow initializer forms everywhere, but only allow init
properties to be initialized when the method is marked with Factory
. However, almost all uses
of this syntax would be a mutation of the receiver, and it may not be clear that the initializer
syntax produces a mutation.
As to whether null
should be a valid return: most people think no. Since almost all initializer
forms dereference the receiver, this is essentially creating a very obscure way of producing
exceptions. In addition, all struct values should be permitted, as they are all safe. default
should be legal if the target type is known to be a struct. We have not considered what the
behavior should be for unconstrained generics.
There also some concerns about the syntactic extensions. First in that this would make identifier { ... }
a valid syntactic form in most situations. This may not be syntactically ambiguous today,
but we have a lot of different features, like block expressions
, which share some syntactic
similarity. Even if there is no syntactic ambiguity, some people are still concerned that the feature
will be too opaque. One way to remedy this would be to require the new
keyword for this form as well.
So the new syntax would be:
var s = new Create() { Name = "Andy" };
There could be some naming ambiguity here because Create
could be either a factory method or a
type name. We would have to preserve the interpretation as a type here for compatibility.
There's a broader question of how or if we'd like a general initializer feature. There's some
question of whether the feature is useful enough to deserve the complexity at all, using any
additional syntax. Alternatively, we could embrace the syntax requiring the new
keyword.
One important piece of history is that initializers are not meant for mutating existing state, only for mutating new objects. This doesn't necessarily conflict with allowing initializers on any object, but the reason here is not that the language is suggesting using object initializers for arbitrary mutation, but that convention alone is good enough to promote use on "new" objects only.
Regardless of the extensions of the feature, we certainly need to implement something for
records. The core feature requirement here is for the with
expression, which needs to assign to
init
fields. We can head two directions: special case the Clone
method, or build a more general
feature. This is a spectrum, where one end may be a new syntactic form specific to just the Clone
method, and the other end could be a Factory
attribute that could be applied to any method.
Conclusion
Right now we're more concerned with what to do for records. In the meantime, let's not support
user-written Clone methods. A Clone method will be generated for a record with an unspeakable type
and the SpecialName flag. The with
expression will look for exactly that method name. We intend
to decide for C# 9 how that method will be written in source. We'll consider broader Factory
scenarios later.