You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Allow fields to be scoped within the accessor body of a property or event declaration.
Motivation
Properties often encapsulate constraints on field access or ensure that certain behaviors are invoked. Sometimes it is appropriate for all field access to be performed through the property, including those from within the same class.
These constraints may be unclear or forgotten, leading to subtle bugs during maintenance which are often difficult to diagnose.
Scoping fields to the body of their property/event would allow the designer to more effectively communicate this intent to encapsulate.
Detailed design
Property-scoped fields may be defined above the accessor list within the body of a property or event:
publicstringMyProperty{stringmyField;
get {returnmyField;}
set
{myField=value;
NotifyOfPropertyChange(nameof(MyProperty));}}
myField and handler are encapsulated within their respective property/event. In each case, both accessors may directly reference the fields. Nothing outside of the property/event scope has direct access to those fields.
First, this is just state that's available to this single property that the value is scoped to. In that regard, it's similar to a 'local'. Just a 'local' that is available to all the accessors. As such, this is how i've been designing things from teh language level:
You can have any number of property locals declared with the property.
The property locals can come before/after/interspersed with the actual property accessors.
The property locals are scoped to that property alone. Other members of the class/struct cannot interact with them.
Subclasses can't interact with them (including overrides of the property).
'var' can be used for these property locals.
Indexers should also be able to use these sorts of property locals.
Now (while this is out of the scope of the actual language change), here's how i would expect to emit them.
These would simply be fields within the same class/struct that the property/indexer is contained within.
The fields would be private.
The fields would be named in such a way that they could never collide with an actual class member. likely something wonky <>_propname_localname
Alternatives
Automatic backing field
This implementation would be an extension of automatic properties. The backing field could be accessed through a keyword field, but only within the scope of the property/event:
publicstringMyProperty{
get {returnfield;}
set
{field=value;
NotifyOfPropertyChange(nameof(MyProperty));}}
This would not allow multiple fields to be encapsulated in the same property.
dotnet/roslyn#7614 has a similar proposal with a different keyword ($state).
Auto-property syntax for custom setter/getter logic
Allow attributes on these locals? It wouldn't be hard to support, and there could be value in putting attributes on the final fields emitted. This does leak through though the implementation of how we deal with property locals.
2. Allow these for events? Yes
How are we going to handle the syntax changes here in our API. It's non trivial to modify the syntactic constructs here in a non-breaking fashion.
We've added 'local functions' to C#7. Should we allow "property local functions" as well?
Should we allow other 'local' constructs? We could have local methods/types/etc. within a property. Technically it would all be possible.
Syntax
It was noted that adding fields above accessors or below accessors (but not interspersed) could be an easier change to the existing compiler design. @lachbaerprovided a potential design which would not be a breaking change to the API:
<!-- this Node hase been added, it looks exactly like "FieldDeclarationSyntax" -->
<NodeName="PropertyLocalFieldDeclarationSyntax"Base="BaseFieldDeclarationSyntax">
<KindName="PropertyLocalFieldDeclaration"/>
<FieldName="AttributeLists"Type="SyntaxList<AttributeListSyntax>"Override="true">
<PropertyComment>
<summary>Gets the attribute declaration list.</summary>
</PropertyComment>
</Field>
<FieldName="Modifiers"Type="SyntaxList<SyntaxToken>"Override="true">
<PropertyComment>
<summary>Gets the modifier list.</summary>
</PropertyComment>
</Field>
<FieldName="Declaration"Type="VariableDeclarationSyntax"Override="true"/>
<FieldName="SemicolonToken"Type="SyntaxToken"Override="true">
<KindName="SemicolonToken"/>
</Field>
</Node>
<NodeName="AccessorListSyntax"Base="CSharpSyntaxNode">
<KindName="AccessorList"/>
<FieldName="OpenBraceToken"Type="SyntaxToken">
<KindName="OpenBraceToken"/>
</Field>
<!-- next line has been added -->
<FieldName="LocalFields"Type="SyntaxList<PropertyLocalFieldDeclarationSyntax>"Optional="true" />
<FieldName="Accessors"Type="SyntaxList<AccessorDeclarationSyntax>"/>
<FieldName="CloseBraceToken"Type="SyntaxToken">
<KindName="CloseBraceToken"/>
</Field>
</Node>
Field name collisions
Should a property-scoped field be allowed to have the same name as another class member?
Should local identifiers within accessors be allowed to have the same name as a property-scoped field?
Property-Scoped Fields
(Ported from dotnet/roslyn#850)
Discuss this issue at: #8632
Summary
Allow fields to be scoped within the accessor body of a property or event declaration.
Motivation
Properties often encapsulate constraints on field access or ensure that certain behaviors are invoked. Sometimes it is appropriate for all field access to be performed through the property, including those from within the same class.
These constraints may be unclear or forgotten, leading to subtle bugs during maintenance which are often difficult to diagnose.
Scoping fields to the body of their property/event would allow the designer to more effectively communicate this intent to encapsulate.
Detailed design
Property-scoped fields may be defined above the accessor list within the body of a property or event:
myField
andhandler
are encapsulated within their respective property/event. In each case, both accessors may directly reference the fields. Nothing outside of the property/event scope has direct access to those fields.Implementation comments from @CyrusNajmabadi:
Alternatives
Automatic backing field
This implementation would be an extension of automatic properties. The backing field could be accessed through a keyword
field
, but only within the scope of the property/event:This would not allow multiple fields to be encapsulated in the same property.
dotnet/roslyn#7614 has a similar proposal with a different keyword (
$state
).Auto-property syntax for custom setter/getter logic
dotnet/roslyn#1551
Semi-auto-properties with setters
dotnet/roslyn#8364
Class-scoped blocks for fields / explicit field usage
dotnet/roslyn#12361
Unresolved questions
A list of questions from @CyrusNajmabadi:
Syntax
It was noted that adding fields above accessors or below accessors (but not interspersed) could be an easier change to the existing compiler design. @lachbaer provided a potential design which would not be a breaking change to the API:
Field name collisions
Should a property-scoped field be allowed to have the same name as another class member?
Should local identifiers within accessors be allowed to have the same name as a property-scoped field?
Design Meetings
The text was updated successfully, but these errors were encountered: