dotnet / dotnet/csharplang

[Proposal]: Property-Scoped Fields

Open
#8,633 0 comments 0 reactions 1 assignee Claimed by @CyrusNajmabadi View on GitHub
Proposal champion
Dominant language
C#
Stars
12.7k
Forks
1.1k
Avg merge
11h 1m
Merged PRs (30d)
3

Description

# Property-Scoped Fields

(Ported from dotnet/roslyn#850)

Discuss this issue at: https://github.com/dotnet/csharplang/discussions/8632

## Summary
[summary]: #summary

Allow fields to be scoped within the accessor body of a property or event declaration.

## Motivation
[motivation]: #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
[design]: #detailed-design

Property-scoped fields may be defined above the accessor list within the body of a property or event:

``` c#
public string MyProperty
{
string myField;

get { return myField; }
set
{
myField = value;
NotifyOfPropertyChange(nameof(MyProperty));
}
}
```

``` c#
public event EventHandler MyHandler
{
EventHandler handler;

add
{
handler += value;
Logger.Log("Added handler");
}
remove
{
handler -= value;
Logger.Log("Removed handler");
}
}
```

`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.

[Implementation comments](https://github.com/dotnet/roslyn/issues/850#issuecomment-183751229) from @CyrusNajmabadi:
> 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:
>
> 1. You can have any number of property locals declared with the property.
> 2. The property locals can come before/after/interspersed with the actual property accessors.
> 3. The property locals are scoped to that property alone. Other members of the class/struct cannot interact with them.
> 4. Subclasses can't interact with them (including overrides of the property).
> 5. 'var' can be used for these property locals.
> 6. 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.
>
> 1. These would simply be fields within the same class/struct that the property/indexer is contained within.
> 2. The fields would be private.
> 3. 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
[alternatives]: #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:

``` c#
public string MyProperty
{
get { return field; }
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
dotnet/roslyn#1551

### Semi-auto-properties with setters
dotnet/roslyn#8364

### Class-scoped blocks for fields / explicit field usage
dotnet/roslyn#12361

## Unresolved questions
[unresolved]: #unresolved-questions

A [list of questions](https://github.com/dotnet/roslyn/issues/850#issuecomment-183751229) from @CyrusNajmabadi:

> 1. 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

> 3. 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.
> 4. We've added 'local functions' to C#7. Should we allow "property local functions" as well?
> 5. 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. @lachbaer [provided a potential design](https://github.com/dotnet/roslyn/issues/850#issuecomment-183873389) which would not be a breaking change to the API:

```xml




Gets the attribute declaration list.




Gets the modifier list.


















```

### 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

- https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-09-26.md#ungrouped

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.