Skip to content

Update documents for field backed properties #43261

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

Merged
merged 12 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion docfx.json
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@
"_csharplang/proposals/csharp-10.0/*.md": "08/07/2021",
"_csharplang/proposals/csharp-11.0/*.md": "09/30/2022",
"_csharplang/proposals/csharp-12.0/*.md": "08/15/2023",
"_csharplang/proposals/*.md": "08/30/2024",
"_csharplang/proposals/*.md": "10/31/2024",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "11/08/2022",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "09/26/2023",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "06/26/2024",
Expand Down Expand Up @@ -669,6 +669,7 @@
"_csharplang/proposals/csharp-13.0/ref-struct-interfaces.md": "Allow ref struct types to implement some interfaces",
"_csharplang/proposals/csharp-13.0/partial-properties.md": "All partial properties and indexers",
"_csharplang/proposals/csharp-13.0/overload-resolution-priority.md": "Overload resolution priority tiebreaker attribute",
"_csharplang/proposals/field-keyword.md": "The `field` contextual keyword",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "C# compiler breaking changes since C# 10",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "C# compiler breaking changes since C# 11",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "C# compiler breaking changes since C# 12",
Expand Down Expand Up @@ -791,6 +792,7 @@
"_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",
"_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.",
"_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.",
"_csharplang/proposals/field-keyword.md": "This proposal introduces a new keyword, `field`, that accesses the compiler generated backing field in a property accessor.",
"_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",
"_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",
"_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",
Expand All @@ -816,6 +818,7 @@
"_csharplang/proposals/csharp-10.0/*.md": "C# feature specifications",
"_csharplang/proposals/csharp-11.0/*.md": "C# feature specifications",
"_csharplang/proposals/csharp-12.0/*.md": "C# feature specifications",
"_csharplang/proposals/csharp-13.0/*.md": "C# feature specifications",
"_csharplang/proposals/*.md": "C# feature specifications (preview)",
"docs/framework/**/*.md": ".NET Framework",
"docs/framework/data/adonet/**/*.md": "ADO.NET",
Expand Down
2 changes: 2 additions & 0 deletions docs/core/whats-new/dotnet-9/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ C# 13 ships with the .NET 9 SDK and includes the following new features:
- Partial properties and indexers are now allowed in `partial` types.
- Overload resolution priority allows library authors to designate one overload as better than others.

In addition, C# 13 adds a preview feature: `field` backed properties.

For more information, see [What's new in C# 13](../../../csharp/whats-new/csharp-13.md).

## F# 9
Expand Down
12 changes: 12 additions & 0 deletions docs/csharp/includes/field-preview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
author: BillWagner
ms.author: wiwagn
ms.topic: include
ms.date: 10/30/2024
---

> [!IMPORTANT]
>
> 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.
>
> 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).
2 changes: 1 addition & 1 deletion docs/csharp/language-reference/attributes/general.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ The first <xref:System.AttributeUsageAttribute> argument must be one or more ele

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

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

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

Expand Down
6 changes: 3 additions & 3 deletions docs/csharp/language-reference/builtin-types/record.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ You can use positional parameters to declare properties of a record and to initi

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

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

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.

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

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

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

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

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

### `with` expressions in derived records

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ f1_keywords:
- "CS9255"
- "CS9256"
- "CS9257"
- "CS9258"
- "CS9263"
- "CS9264"
- "CS9266"
helpviewer_keywords:
- "CS0260"
- "CS0261"
Expand Down Expand Up @@ -89,7 +93,10 @@ helpviewer_keywords:
- "CS9255"
- "CS9256"
- "CS9257"
ms.date: 08/21/2024
- "CS9258"
- "CS9263"
- "CS9266"
ms.date: 11/06/2024
---
# Errors and warnings related to `partial` type and `partial` member declarations

Expand Down Expand Up @@ -141,6 +148,13 @@ That's by design. The text closely matches the text of the compiler error / warn
- [**CS9255**](#partial-properties): *Both partial property declarations must have the same type.*
- [**CS9256**](#partial-properties): *Partial property declarations have signature differences.*
- [**CS9257**](#partial-properties): *Both partial property declarations must be required or neither may be required*
- [**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.*
- [**CS9263**](#field-backed-properties): *A partial property cannot have an initializer on both the definition and implementation.*

The following warnings can be generated for field backed properties:

- [**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.**
- [**CS9266**](#field-backed-properties): *One accessor of property should use '`field`' because the other accessor is using it.*

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

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

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

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

```csharp
public partial int ImplementingDeclaration { get => field; set; }
```

## field backed properties

- **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.*
- **CS9263**: *A partial property cannot have an initializer on both the definition and implementation.*
- **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.**
- **CS9266**: *One accessor of property should use '`field`' because the other accessor is using it.*

[!INCLUDE[field-preview](../../includes/field-preview.md)]

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

**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.
26 changes: 26 additions & 0 deletions docs/csharp/language-reference/keywords/field.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
description: "The `field` contextual keyword - access the compiler synthesized backing field for a property"
title: "The `field` contextual keyword"
ms.date: 10/30/2024
f1_keywords:
- "field_CSharpKeyword"
helpviewer_keywords:
- "field keyword [C#]"
---
# `field` - Field backed property declarations

[!INCLUDE[field-preview](../../includes/field-preview.md)]

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.

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:

:::code language="csharp" source="./snippets/PropertyAccessors.cs" id="FieldBackedProperty":::

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.

For more information, see the [Properties](../../programming-guide/classes-and-structs/properties.md) and [Indexers](../../programming-guide/indexers/index.md) articles.

## C# language specification

[!INCLUDE[CSharplangspec](~/includes/csharplangspec-md.md)]
38 changes: 22 additions & 16 deletions docs/csharp/language-reference/keywords/get.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,42 @@
---
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."
title: "get keyword: property accessor"
ms.date: 08/15/2024
title: "The get keyword: property accessor"
ms.date: 10/30/2024
f1_keywords:
- "get_CSharpKeyword"
- "get"
helpviewer_keywords:
- "get keyword [C#]"
---
# get (C# Reference)
# The `get` keyword

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

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.

:::code language="csharp" source="./snippets/PropertyAccessors.cs" id="GetSetAccessors":::

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.

:::code language="csharp" source="./snippets/PropertyAccessors.cs" id="GetSetExpressions":::
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).

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.

:::code language="csharp" source="./snippets/PropertyAccessors.cs" id="AutoImplementedProperties":::

> [!IMPORTANT]
> 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.

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.

:::code language="csharp" source="./snippets/PropertyAccessors.cs" id="GetSetExpressions":::

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:

:::code language="csharp" source="./snippets/PropertyAccessors.cs" id="FieldBackedProperty":::

[!INCLUDE[field-preview](../../includes/field-preview.md)]

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.

:::code language="csharp" source="./snippets/PropertyAccessors.cs" id="GetSetAccessors":::

## C# Language Specification

[!INCLUDE[CSharplangspec](~/includes/csharplangspec-md.md)]
[!INCLUDE[CSharplangspec](~/includes/csharplangspec-md.md)]

## See also

- [C# Keywords](./index.md)
Expand Down
Loading
Loading