-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: Allow record constructors and properties to have different access modifiers to that of the record itself #8798
Comments
That seems very easy to miss. What if instead of allowing this constructor signature to be placed somewhere inside the class, a modifier was permitted between the
that is: class-declaration
: attributes?
class-modifiers?
partial?
'class'
identifier
type-parameter-list?
record-parameters?
class-base?
type-parameter-constraints-clauses?
class-body becomes class-declaration
: attributes?
class-modifiers?
partial?
'class'
identifier
type-parameter-list?
attributes?
constructor-modifiers?
record-parameters?
class-base?
type-parameter-constraints-clauses?
class-body |
What is the reason for making said type a "record"? Just to have more short-hand for the properties? I'd argue that a tenet of being a record is more than just the shape, it's also the usage. A record is expected to be publicly creatable. Can you identify a programming language that contains record types where the constructor can have a narrower accessibility than the type itself? F#, for example, doesn't allow this. |
That's the accessibility of the record type itself. F# doesn't allow defining a public record that has a constructor with a different accessibility modifier. |
@HaloFour Yes, F# does allow it. The accessibility modifier for the type precedes the name: |
Hm, you're correct, you can have two accessibility modifiers. However, that second one affects both the constructor and the record properties, so you'd end up with the following: public sealed class T {
internal int _P;
internal T() { }
internal int P { get; set; }
} |
@HaloFour Correct. That allows you to treat the type as a record internally, but expose it differently externally (i.e., through type extensions or module-scoped functions). |
Gotcha. In this example it doesn't appear that the properties are intended to be exposed, even internally, nor does it appear that the shape of the type would be utilized in pattern matching, so I would still argue that the type is probably not a good candidate for being a record type. This proposal seems to more seek to push primary constructors as an alternate short-hand for defining general purpose types. |
@HaloFour This syntax suggested by @bbarry is totally reasonable why not? |
Primarily because I think that "why not?" is not a sufficient reason to implement any feature. 😄 I don't particularly have any issues with this proposal, I just think that every idea needs to be sufficient beat up in order to demonstrate why it's useful enough to be worth the effort in implementing it and support it. As it stands the record proposal explicitly states that the constructor and the properties are to be The one thing I'd really like to not see is the big discussion to make primary constructors become a full-fledged alternate class definition syntax, which is what happened on CodePlex. I'd like to see an example case that better fits the proposal, one where the type is actually intended to be used as a record and those fields have a reason to be exposed as properties, even internally. |
@DavidArno The title should be "allow access modifiers for record constructors" it doesn't make sense to restrict it to only class Foo private (Bar bar : Bar, Baz baz : Baz) {
public static Foo Create() { ... }
} So |
@HaloFour See the example in my previous comment.
I wasn't talking about properties' access modifiers, or immutability, they will be public and immutable by default. This proposal is about the record's primary constructor access modifier, period. |
You have a good point in that my example code could very well be an abuse of record types. That's not something I'd considered. As you point out, a record would have side effects, such as exposing the fields via properties. @bbarry hits upon a really nice syntax that I hadn't considered that could have other benefits. If I have a record type that can be exposed via an API, it needs to be public. However, I might want to treat it as a token: external code can accept a copy, pass it around and ultimately pass it back. I therefore might want the properties to be internal. So I may have accidentally proposed a solution to a need quite different to my own. |
I meant a real world example, but I do see the utility of a factory method for creating records. However, I think that the following behavior may not be particularly intuitive: var foo = new Foo(bar, baz); // compiler error
if (foo is Foo(bar, baz)) { ... } // ok |
@HaloFour That's currently the case, var foo = new Foo(bar, baz); // error
if (foo is Foo) { ... } // ok Extended |
I've updated the title as suggested. I'm still a little unsure whether I should close this proposal though as my initial use case doesn't really fit the new title so well. |
@DavidArno I'm not native but it doesn't sound grammatically correct 😄 anyway, I wasn't talking about properties' access modifiers, that complicates a lot of other things like |
It isn't grammatically correct! I tried to edit it on my phone and messed it up! I'll re-edit it when I'm at a computer :) |
We are now taking language feature discussion in other repositories:
Features that are under active design or development, or which are "championed" by someone on the language design team, have already been moved either as issues or as checked-in design documents. For example, the proposal in this repo "Proposal: Partial interface implementation a.k.a. Traits" (issue 16139 and a few other issues that request the same thing) are now tracked by the language team at issue 52 in https://github.com/dotnet/csharplang/issues, and there is a draft spec at https://github.com/dotnet/csharplang/blob/master/proposals/default-interface-methods.md and further discussion at issue 288 in https://github.com/dotnet/csharplang/issues. Prototyping of the compiler portion of language features is still tracked here; see, for example, https://github.com/dotnet/roslyn/tree/features/DefaultInterfaceImplementation and issue 17952. In order to facilitate that transition, we have started closing language design discussions from the roslyn repo with a note briefly explaining why. When we are aware of an existing discussion for the feature already in the new repo, we are adding a link to that. But we're not adding new issues to the new repos for existing discussions in this repo that the language design team does not currently envision taking on. Our intent is to eventually close the language design issues in the Roslyn repo and encourage discussion in one of the new repos instead. Our intent is not to shut down discussion on language design - you can still continue discussion on the closed issues if you want - but rather we would like to encourage people to move discussion to where we are more likely to be paying attention (the new repo), or to abandon discussions that are no longer of interest to you. If you happen to notice that one of the closed issues has a relevant issue in the new repo, and we have not added a link to the new issue, we would appreciate you providing a link from the old to the new discussion. That way people who are still interested in the discussion can start paying attention to the new issue. Also, we'd welcome any ideas you might have on how we could better manage the transition. Comments and discussion about closing and/or moving issues should be directed to #18002. Comments and discussion about this issue can take place here or on an issue in the relevant repo. I am not moving this particular issue because the current draft proposal for records already supports this:
|
The original proposal was for some means of specifying a public record, with an internal constructor. My example below is arguably a poor one, as it could be viewed a misuse of records. Further, as per F#, it could be useful to specify the access modifier for properties too. These points are covered in subsequent comments, so I've left my original proposal as is.
Consider the following real-world piece of code:
With records, I'd be able to almost replace the above with:
There would be one, very important, difference though: the constructor would now be public. It would therefore be useful if the accessibility of the constructor could be specified for a record, if it needs to be different to the record itself. Possibly something like:
This would allow the boilerplate-avoidance features of records to be utilised, without compromising the ability to specify the accessibility of the constructor.
The text was updated successfully, but these errors were encountered: