Skip to content

Commit 8aeac83

Browse files
Update documents for field backed properties (#43261)
* Add field backed properties to lang reference Add the field backed properties updates to property articles in the language reference. While doing this, update the relevant articles to lead with automatically implemented properties, followed by field backed, then expression bodied, and finally fully implemented property accessors. * Autoimplemented -> automatically implemented Replace "autoimplemented" with "automatically implemented" * Finish conceptual docs * Add new errors and warnings The `field` backed property preview feature adds two new diagnostics. * "compiler synthesized" Use the term "compiler synthesized" rather than "compiler generated" for the backing field in automatically implemented or field backed properties. * markdown lint error * build issues * name conflict * Add additional diagnostics A couple warnings have been added regarding field backed properties. * Update docs/csharp/programming-guide/classes-and-structs/auto-implemented-properties.md Co-authored-by: David Pine <[email protected]> * Update docs/csharp/programming-guide/classes-and-structs/properties.md Co-authored-by: David Pine <[email protected]> * Add text in the .NET 9 file Add note on field backed properties for field backed properties. --------- Co-authored-by: David Pine <[email protected]>
1 parent ebc69da commit 8aeac83

File tree

29 files changed

+316
-108
lines changed

29 files changed

+316
-108
lines changed

docfx.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,7 @@
493493
"_csharplang/proposals/csharp-10.0/*.md": "08/07/2021",
494494
"_csharplang/proposals/csharp-11.0/*.md": "09/30/2022",
495495
"_csharplang/proposals/csharp-12.0/*.md": "08/15/2023",
496-
"_csharplang/proposals/*.md": "08/30/2024",
496+
"_csharplang/proposals/*.md": "10/31/2024",
497497
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "11/08/2022",
498498
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "09/26/2023",
499499
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "06/26/2024",
@@ -670,6 +670,7 @@
670670
"_csharplang/proposals/csharp-13.0/ref-struct-interfaces.md": "Allow ref struct types to implement some interfaces",
671671
"_csharplang/proposals/csharp-13.0/partial-properties.md": "All partial properties and indexers",
672672
"_csharplang/proposals/csharp-13.0/overload-resolution-priority.md": "Overload resolution priority tiebreaker attribute",
673+
"_csharplang/proposals/field-keyword.md": "The `field` contextual keyword",
673674
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "C# compiler breaking changes since C# 10",
674675
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "C# compiler breaking changes since C# 11",
675676
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "C# compiler breaking changes since C# 12",
@@ -792,6 +793,7 @@
792793
"_csharplang/proposals/csharp-13.0/ref-struct-interfaces.md": "This proposal provides features that enable interface authors to allow `ref struct` types to implement a particular interface",
793794
"_csharplang/proposals/csharp-13.0/partial-properties.md": "This proposal provides for partial properties and indexers, allowing the definition of a property or indexer to be split across multiple parts.",
794795
"_csharplang/proposals/csharp-13.0/overload-resolution-priority.md": "This proposal introduces a new attribute, `OverloadResolutionPriorityAttribute`, that can be applied to methods to influence overload resolution.",
796+
"_csharplang/proposals/field-keyword.md": "This proposal introduces a new keyword, `field`, that accesses the compiler generated backing field in a property accessor.",
795797
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "Learn about any breaking changes since the initial release of C# 10 and included in C# 11",
796798
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "Learn about any breaking changes since the initial release of C# 11 and included in C# 12",
797799
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "Learn about any breaking changes since the initial release of C# 12 and included in C# 13",
@@ -817,6 +819,7 @@
817819
"_csharplang/proposals/csharp-10.0/*.md": "C# feature specifications",
818820
"_csharplang/proposals/csharp-11.0/*.md": "C# feature specifications",
819821
"_csharplang/proposals/csharp-12.0/*.md": "C# feature specifications",
822+
"_csharplang/proposals/csharp-13.0/*.md": "C# feature specifications",
820823
"_csharplang/proposals/*.md": "C# feature specifications (preview)",
821824
"docs/framework/**/*.md": ".NET Framework",
822825
"docs/framework/data/adonet/**/*.md": "ADO.NET",

docs/core/whats-new/dotnet-9/overview.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ C# 13 ships with the .NET 9 SDK and includes the following new features:
146146
- Partial properties and indexers are now allowed in `partial` types.
147147
- Overload resolution priority allows library authors to designate one overload as better than others.
148148

149+
In addition, C# 13 adds a preview feature: `field` backed properties.
150+
149151
For more information, see [What's new in C# 13](../../../csharp/whats-new/csharp-13.md).
150152

151153
## F# 9
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
author: BillWagner
3+
ms.author: wiwagn
4+
ms.topic: include
5+
ms.date: 10/30/2024
6+
---
7+
8+
> [!IMPORTANT]
9+
>
10+
> The `field` keyword is a preview feature in C# 13. You must be using .NET 9 and set your `<LangVersion>` element to `preview` in your project file in order to use the `field` contextual keyword.
11+
>
12+
> You should be careful using the `field` keyword feature in a class that has a field named `field`. The new `field` keyword shadows a field named `field` in the scope of a property accessor. You can either change the name of the `field` variable, or use the `@` token to reference the `field` identifier as `@field`. You can learn more by reading the feature specification for [the `field` keyword](~/_csharplang/proposals/field-keyword.md).

docs/csharp/language-reference/attributes/general.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ The first <xref:System.AttributeUsageAttribute> argument must be one or more ele
104104

105105
:::code language="csharp" source="snippets/NewPropertyOrFieldAttribute.cs" ID="SnippetDefinePropertyAttribute" :::
106106

107-
Attributes can be applied to either the property or the backing field for an autoimplemented property. The attribute applies to the property, unless you specify the `field` specifier on the attribute. Both are shown in the following example:
107+
Attributes can be applied to either the property or the backing field for an automatically implemented property. The attribute applies to the property, unless you specify the `field` specifier on the attribute. Both are shown in the following example:
108108

109109
:::code language="csharp" source="snippets/NewPropertyOrFieldAttribute.cs" ID="SnippetUsePropertyAttribute" :::
110110

docs/csharp/language-reference/builtin-types/record.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ You can use positional parameters to declare properties of a record and to initi
5757

5858
When you use the positional syntax for property definition, the compiler creates:
5959

60-
* A public autoimplemented property for each positional parameter provided in the record declaration.
60+
* A public automatically implemented property for each positional parameter provided in the record declaration.
6161
- For `record` types and `readonly record struct` types: An [init-only](../keywords/init.md) property.
6262
- For `record struct` types: A read-write property.
6363
* A primary constructor whose parameters match the positional parameters on the record declaration.
@@ -70,7 +70,7 @@ You may want to add attributes to any of these elements the compiler creates fro
7070

7171
The preceding example also shows how to create XML documentation comments for the record. You can add the `<param>` tag to add documentation for the primary constructor's parameters.
7272

73-
If the generated autoimplemented property definition isn't what you want, you can define your own property of the same name. For example, you may want to change accessibility or mutability, or provide an implementation for either the `get` or `set` accessor. If you declare the property in your source, you must initialize it from the positional parameter of the record. If your property is an autoimplemented property, you must initialize the property. If you add a backing field in your source, you must initialize the backing field. The generated deconstructor uses your property definition. For instance, the following example declares the `FirstName` and `LastName` properties of a positional record `public`, but restricts the `Id` positional parameter to `internal`. You can use this syntax for records and record struct types.
73+
If the generated automatically implemented property definition isn't what you want, you can define your own property of the same name. For example, you may want to change accessibility or mutability, or provide an implementation for either the `get` or `set` accessor. If you declare the property in your source, you must initialize it from the positional parameter of the record. If your property is an automatically implemented property, you must initialize the property. If you add a backing field in your source, you must initialize the backing field. The generated deconstructor uses your property definition. For instance, the following example declares the `FirstName` and `LastName` properties of a positional record `public`, but restricts the `Id` positional parameter to `internal`. You can use this syntax for records and record struct types.
7474

7575
:::code language="csharp" source="snippets/shared/RecordType.cs" id="PositionalWithManualProperty":::
7676

@@ -185,7 +185,7 @@ In the example, all variables are declared as `Person`, even when the instance i
185185

186186
To implement this behavior, the compiler synthesizes an `EqualityContract` property that returns a <xref:System.Type> object that matches the type of the record. The `EqualityContract` enables the equality methods to compare the runtime type of objects when they're checking for equality. If the base type of a record is `object`, this property is `virtual`. If the base type is another record type, this property is an override. If the record type is `sealed`, this property is effectively `sealed` because the type is `sealed`.
187187

188-
When code compares two instances of a derived type, the synthesized equality methods check all data members of the base and derived types for equality. The synthesized `GetHashCode` method uses the `GetHashCode` method from all data members declared in the base type and the derived record type. The data members of a `record` include all declared fields and the compiler-synthesized backing field for any autoimplemented properties.
188+
When code compares two instances of a derived type, the synthesized equality methods check all data members of the base and derived types for equality. The synthesized `GetHashCode` method uses the `GetHashCode` method from all data members declared in the base type and the derived record type. The data members of a `record` include all declared fields and the compiler-synthesized backing field for any automatically implemented properties.
189189

190190
### `with` expressions in derived records
191191

docs/csharp/language-reference/compiler-messages/partial-declarations.md

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ f1_keywords:
4545
- "CS9255"
4646
- "CS9256"
4747
- "CS9257"
48+
- "CS9258"
49+
- "CS9263"
50+
- "CS9264"
51+
- "CS9266"
4852
helpviewer_keywords:
4953
- "CS0260"
5054
- "CS0261"
@@ -89,7 +93,10 @@ helpviewer_keywords:
8993
- "CS9255"
9094
- "CS9256"
9195
- "CS9257"
92-
ms.date: 08/21/2024
96+
- "CS9258"
97+
- "CS9263"
98+
- "CS9266"
99+
ms.date: 11/06/2024
93100
---
94101
# Errors and warnings related to `partial` type and `partial` member declarations
95102

@@ -141,6 +148,13 @@ That's by design. The text closely matches the text of the compiler error / warn
141148
- [**CS9255**](#partial-properties): *Both partial property declarations must have the same type.*
142149
- [**CS9256**](#partial-properties): *Partial property declarations have signature differences.*
143150
- [**CS9257**](#partial-properties): *Both partial property declarations must be required or neither may be required*
151+
- [**CS9258**](#field-backed-properties): *In this language version, the '`field`' keyword binds to a synthesized backing field for the property. To avoid generating a synthesized backing field, and to refer to the existing member, use '`this.field`' or '`@field`' instead.*
152+
- [**CS9263**](#field-backed-properties): *A partial property cannot have an initializer on both the definition and implementation.*
153+
154+
The following warnings can be generated for field backed properties:
155+
156+
- [**CS9264**](#field-backed-properties): *Non-nullable property must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or adding '`[field: MaybeNull, AllowNull]`' attributes.**
157+
- [**CS9266**](#field-backed-properties): *One accessor of property should use '`field`' because the other accessor is using it.*
144158

145159
The following sections explain the cause and fixes for these errors and warnings.
146160

@@ -229,4 +243,21 @@ The following warning indicates a signature difference in the declaring and impl
229243

230244
- **CS9256**: *Partial property declarations have signature differences.*
231245

232-
A partial property or indexer must have both a *declaring declaration* and an *implementing declaration*. The signatures for both declarations must match. Because the *declaring declaration* uses the same syntax as an automatically implemented property, the *implementing declaration* can't be an automatically implemented property. The accessors must have bodies.
246+
A partial property or indexer must have both a *declaring declaration* and an *implementing declaration*. The signatures for both declarations must match. Because the *declaring declaration* uses the same syntax as an automatically implemented property, the *implementing declaration* can't be an automatically implemented property. The accessors must have at least one accessor body. Beginning in C# 13, you can use the [`field`](../keywords/field.md) keyword to declare one accessor using a concise syntax:
247+
248+
```csharp
249+
public partial int ImplementingDeclaration { get => field; set; }
250+
```
251+
252+
## field backed properties
253+
254+
- **CS9258**: *In this language version, the '`field`' keyword binds to a synthesized backing field for the property. To avoid generating a synthesized backing field, and to refer to the existing member, use '`this.field`' or '`@field`' instead.*
255+
- **CS9263**: *A partial property cannot have an initializer on both the definition and implementation.*
256+
- **CS9264**: *Non-nullable property must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or adding '`[field: MaybeNull, AllowNull]`' attributes.**
257+
- **CS9266**: *One accessor of property should use '`field`' because the other accessor is using it.*
258+
259+
[!INCLUDE[field-preview](../../includes/field-preview.md)]
260+
261+
Beginning with C# 13, the preview feature, `field` backed properties allows you to access the compiler synthesized backing field for a property. **CS9258** indicates that you have a variable named `field`, which can be hidden by the contextual keyword `field`.
262+
263+
**CS9263** indicates that your declaring declaration includes an implementation. That implementation might be accessing the compiler synthesized backing field for that property. **CS9264** indicates that the your use of `field` assumes a non-nullable backing field while the property declaration is nullable. The compiler assumes both the backing field and the property have the same nullability. You need to add the `[field:MaybeNull, AllowNull]` attribute to the property declaration to indicate that the `field` value should be considered nullable. **CS9266** indicates that one of a properties accessors uses the `field` keyword, but the other uses a hand-declared backing field. The warning indicates you may have done that by accident.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
description: "The `field` contextual keyword - access the compiler synthesized backing field for a property"
3+
title: "The `field` contextual keyword"
4+
ms.date: 10/30/2024
5+
f1_keywords:
6+
- "field_CSharpKeyword"
7+
helpviewer_keywords:
8+
- "field keyword [C#]"
9+
---
10+
# `field` - Field backed property declarations
11+
12+
[!INCLUDE[field-preview](../../includes/field-preview.md)]
13+
14+
The contextual keyword `field`, added as a preview feature in C# 13, can be used in a property accessor to access the compiler synthesized backing field of a property. This syntax enables you to define the body of a `get` or `set` accessor and let the compiler generate the other accessor as it would in an automatically implemented property.
15+
16+
The addition of the `field` contextual keywords provides a smooth path to add benefits such as range checking to an automatically implemented property. This practice is shown in the following example:
17+
18+
:::code language="csharp" source="./snippets/PropertyAccessors.cs" id="FieldBackedProperty":::
19+
20+
You might implement the `Hours` property as an automatically implemented property. Then, you discover that you want to protect against a negative value. You use `field` and provide range checking in the `set` accessor. You don't need to declare the backing field by hand and provide a body for the `get` accessor.
21+
22+
For more information, see the [Properties](../../programming-guide/classes-and-structs/properties.md) and [Indexers](../../programming-guide/indexers/index.md) articles.
23+
24+
## C# language specification
25+
26+
[!INCLUDE[CSharplangspec](~/includes/csharplangspec-md.md)]

docs/csharp/language-reference/keywords/get.md

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,42 @@
11
---
22
description: "The C# get keyword declares a get accessor in a property or indexer. It defines the code to retrieve the value of the property or indexed property."
3-
title: "get keyword: property accessor"
4-
ms.date: 08/15/2024
3+
title: "The get keyword: property accessor"
4+
ms.date: 10/30/2024
55
f1_keywords:
66
- "get_CSharpKeyword"
77
- "get"
88
helpviewer_keywords:
99
- "get keyword [C#]"
1010
---
11-
# get (C# Reference)
11+
# The `get` keyword
1212

13-
The `get` keyword defines an *accessor* method in a property or indexer that returns the property value or the indexer element. For more information, see [Properties](../../programming-guide/classes-and-structs/properties.md), [Automatically implemented Properties](../../programming-guide/classes-and-structs/automatically implemented-properties.md), and [Indexers](../../programming-guide/indexers/index.md).
14-
15-
The following example defines both a `get` and a `set` accessor for a property named `Seconds`. It uses a private field named `_seconds` to back the property value.
16-
17-
:::code language="csharp" source="./snippets/PropertyAccessors.cs" id="GetSetAccessors":::
18-
19-
Often, the `get` accessor consists of a single statement that returns a value, as it did in the previous example. You can implement the `get` accessor as an expression-bodied member. The following example implements both the `get` and the `set` accessor as expression-bodied members.
20-
21-
:::code language="csharp" source="./snippets/PropertyAccessors.cs" id="GetSetExpressions":::
13+
The `get` keyword defines an *accessor* method in a property or indexer that returns the property value or the indexer element. For more information, see [Properties](../../programming-guide/classes-and-structs/properties.md), [Automatically implemented Properties](../../programming-guide/classes-and-structs/automatically implemented-properties.md), and [Indexers](../../programming-guide/indexers/index.md).
2214

2315
For simple cases in which a property's `get` and `set` accessors perform no other operation than setting or retrieving a value in a private backing field, you can take advantage of the C# compiler's support for automatically implemented properties. The following example implements `Hours` as an automatically implemented property.
24-
16+
2517
:::code language="csharp" source="./snippets/PropertyAccessors.cs" id="AutoImplementedProperties":::
26-
18+
2719
> [!IMPORTANT]
2820
> Automatically implemented properties aren't allowed for [interface property declarations](../../programming-guide/classes-and-structs/interface-properties.md) or the implementing declaration for a [partial property](./partial-member.md). The compiler interprets syntax matching an automatically implemented property as the declaring declaration, not an implementing declaration.
2921
22+
Often, the `get` accessor consists of a single statement that returns a value, as it did in the previous example. You can implement the `get` accessor as an expression-bodied member. The following example implements both the `get` and the `set` accessor as expression-bodied members.
23+
24+
:::code language="csharp" source="./snippets/PropertyAccessors.cs" id="GetSetExpressions":::
25+
26+
You might find that you need to implement one of the accessor bodies. You can use a field backed property to let the compiler generate one accessor while you write the other by hand. You use the `field` keyword, added as a preview feature in C# 13, to access the compiler synthesized backing field:
27+
28+
:::code language="csharp" source="./snippets/PropertyAccessors.cs" id="FieldBackedProperty":::
29+
30+
[!INCLUDE[field-preview](../../includes/field-preview.md)]
31+
32+
The following example defines both a `get` and a `set` accessor for a property named `Seconds`. It uses a private field named `_seconds` to back the property value.
33+
34+
:::code language="csharp" source="./snippets/PropertyAccessors.cs" id="GetSetAccessors":::
35+
3036
## C# Language Specification
3137

32-
[!INCLUDE[CSharplangspec](~/includes/csharplangspec-md.md)]
33-
38+
[!INCLUDE[CSharplangspec](~/includes/csharplangspec-md.md)]
39+
3440
## See also
3541

3642
- [C# Keywords](./index.md)

0 commit comments

Comments
 (0)