Primary constructors in C# 10 #4025
Replies: 10 comments 23 replies
-
That proposed syntax seems to be lost in the declaration. I would prefer something like: default C() { } // no parameters allowed; has access to primary ctor parameters This is similar to |
Beta Was this translation helpful? Give feedback.
-
I vote for Scala solution its commonly known and really simple: |
Beta Was this translation helpful? Give feedback.
-
Constructor parameter inheritance, or the lack-thereof, has been a large ergonomic wart in every OOP language I’ve used to-date - I suspect this will get worse with the addition of primary-constructors - and the introduction of primary-constructors provide an opportunity to have C# “bless” a single specific ctor as the single, one and only (Highlander?) primary ctor, and so eliminate overload selection problems entirely. By “parameter inheritance” - I’m referring to not just the ability for supertype constructors to be pulled-in implicitly as automatically-generated subtype ctors, but that if a supertype’s ctor has, say, 9 parameters, then the subclass should be able to define their own ctor without needing to manually repeat those 9 parameters and pass them through to (It could be hacked-together with the current CLR by using parameter-objects behind-the-scenes, but that has potential to balloon assembly metadata sizes; otherwise doing it “properly” would require .NET to fully support parameter-lists or method-signatures as first-class types in their own right, with support for variance and equivalence (and isomorphism with value-tuples and other types would be nice too). Syntactically, something like this (and appropriating
Assuming the parameter-list (the type) and argument-list (the values) can be reified then this switching from inheritance to composition without requiring the caller to provide their own composed instance - this would be handy with primary-constructors as it means the call-sites of the former-subclass don’t need to be updated:
Where the So the call-site for D could be any of these:
|
Beta Was this translation helpful? Give feedback.
-
you could look at Kotlin as a reference implementation |
Beta Was this translation helpful? Give feedback.
-
Probably it has been discussed before, so a "shut up, Ian!" won't be taken as a personal offense: why primary constructors, instead of member declaration in ordinary constructors, à la TypeScript? I cannot imagine a reason to pick a single constructor as the "chosen one". It makes sense for records, since records binds that two concepts: a primary constructor and automatic member declarations (and yes, all the thingies related to equality and custom identity). Just my two cents, of course. |
Beta Was this translation helpful? Give feedback.
-
For what I understand, primary constructor is more a syntax than a feature record R0(int I); // <- Property I is generated
record R1(int I) : R0(I); // <- Property I isn't generated |
Beta Was this translation helpful? Give feedback.
-
I would rather go with Dart constructor specialized for data/fields initialization: class Car {
String make;
String model;
String yearMade;
bool hasABS;
Car(this.make, this.model, this.yearMade, this.hasABS);
} Current proposal will get a little bit ugly when you add interfaces and generic parameters to the line with class decclaration. |
Beta Was this translation helpful? Give feedback.
-
I've tried to fiddle around Primary constructors in Sharplab, [constructor: SetsRequiredMembers] // <-warning CS0658: 'constructor' is not a recognized attribute location. Valid attribute locations for this declaration are 'type'. All attributes in this block will be ignored.
class C<T>(T value) { } |
Beta Was this translation helpful? Give feedback.
-
Hi friends, I love this feature proposal! One concern I have is the conflict that arises when contemplating how to name constructor parameters, as they're not only parameters but they're also essentially fields. We're mixing metaphors a bit, and when that happens developers tend to argue, specifically over naming conventions. More often than not, fields are prefixed with The currently proposed syntax is as follows: public class Person(string firstName, string lastName)
{
} My concern is that while the parameter names look good, the field names are forced to match that of the parameter. One possible counter-argument is to name your parameters as you'd like your fields to be named: public class Person(string _firstName, string _lastName)
{
} But this conflicts with industry-standard naming conventions for parameters. I'd like to propose an optional syntax to express an alternative name for the field, whilst keeping the name of the parameter. public class Person(string _firstName: firstName, string _lastName: lastName)
{
} This would be an optional syntax, that allows the developer to define their field names. When it comes to the ability to express defaults, the syntax would look like this: public class Person(string _firstName: firstName = "David", string _lastName: lastName = "Pine")
{
} |
Beta Was this translation helpful? Give feedback.
-
[UPDATE]: Opened #7028 suggesting the following. This is a cool feature to have. At the moment, the scope of the constructor parameters is restricted to the body of the constructor and extended on demand via backing fields as references of them begin to appear in the containing class. This, as many have pointed out, could lead to confusion and the behavior would not maintain parity with that of positional records. To be more flexible and explicit, a way to specify the target of the arguments could be implemented. The target can be: field // Assign to a field
get, set // Assign to a read-write Property
get // Assign to a read-only Property
readonly field // Assign to a read-only field The target could be specified in an class Person(string FirstName, string LastName) as get, set
{
}
// OR
class Person(string FirstName, string LastName) params : get, set
{
} |
Beta Was this translation helpful? Give feedback.
-
This discussion is meant to capture open issues around generalized primary constructors beyond C# 9.0 records, for the purposes of discussion in LDM.
There's a slightly dated proposal here: https://github.com/dotnet/csharplang/blob/master/proposals/primary-constructors.md, which isn't too far off, but does not yet reflect decisions made for the records-specific version of the feature in 9.0.
Syntax
I believe we are resolved to the same syntax as we have in records; i.e.:
Semantics
Primary constructors outside of records should not lead to the generation of public properties by default. The original (pre-record) intention, described in the linked proposal, was for them to be captured on demand like in a lambda expression, stored in private fields as an implementation detail, but treated as parameters wherever they are referenced. This would mean that you could reference
i
in the above example anywhere in the class body, but you could not reference the backing fieldthis.i
, or for that matterother.i
in the manner of private fields.The records design brings this into question. In records, the semantics are that a member declaration is generated if one of that name does not already exist. A generalized design for primary constructors could follow the same scheme, with the "small" caveat that instead of a public auto-property, the generated member would be a private field of the same name. Also, in record primary constructors, the constructor parameters are in scope in field and property initializers, and shadow the member of the same name. A similar design for general primary constructors would allow for:
Such a design is further from what we originally envisioned, but much closer to what we already have in records; the only difference being the kind of member generated if one is not explicitly declared.
Primary constructor body
Both in and outside records, the lack of a place to declare a constructor body for a primary constructor sooner or later becomes a problem. Falling off the cliff and turning it into an ordinary constructor declaration is jarring.
We have a proposal for a syntax for this:
I.e. the name of the class followed by a body (could also be an expression body). The parameters would be in scope inside the primary constructor declaration, and would shadow the corresponding fields (which can still be accessed with
this.s
etc. if we allow that).We could allow an explicit accessibility modifier on the declaration, which would change the accessibility of the primary constructor.
Direct constructor parameters
The feature has a potential interaction with Direct Constructor Parameters #4024.
Member declarations in primary constructor parameters
If primary constructor parameters can lead to a member being declared, there's a spectrum of possibilities for allowing additional syntax on the parameter to influence that member declaration:
field
target) could transfer to the memberreadonly
,data
orrequired
could transfer to the memberOther open design questions?
Beta Was this translation helpful? Give feedback.
All reactions