diff --git a/docfx.json b/docfx.json index 2c20057892346..661cd80fc9eff 100644 --- a/docfx.json +++ b/docfx.json @@ -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", @@ -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", @@ -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", @@ -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", diff --git a/docs/core/whats-new/dotnet-9/overview.md b/docs/core/whats-new/dotnet-9/overview.md index 9625a15d1deb9..f27f08345ca93 100644 --- a/docs/core/whats-new/dotnet-9/overview.md +++ b/docs/core/whats-new/dotnet-9/overview.md @@ -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 diff --git a/docs/csharp/includes/field-preview.md b/docs/csharp/includes/field-preview.md new file mode 100644 index 0000000000000..b504bd1d668de --- /dev/null +++ b/docs/csharp/includes/field-preview.md @@ -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 `` 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). diff --git a/docs/csharp/language-reference/attributes/general.md b/docs/csharp/language-reference/attributes/general.md index 00855cba57adb..0588558460c82 100644 --- a/docs/csharp/language-reference/attributes/general.md +++ b/docs/csharp/language-reference/attributes/general.md @@ -104,7 +104,7 @@ The first 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" ::: diff --git a/docs/csharp/language-reference/builtin-types/record.md b/docs/csharp/language-reference/builtin-types/record.md index 24c9f5c49dad4..9d4bf638a58c3 100644 --- a/docs/csharp/language-reference/builtin-types/record.md +++ b/docs/csharp/language-reference/builtin-types/record.md @@ -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. @@ -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 `` 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"::: @@ -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 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 diff --git a/docs/csharp/language-reference/compiler-messages/partial-declarations.md b/docs/csharp/language-reference/compiler-messages/partial-declarations.md index 2677cf331725a..18daadb9e82cc 100644 --- a/docs/csharp/language-reference/compiler-messages/partial-declarations.md +++ b/docs/csharp/language-reference/compiler-messages/partial-declarations.md @@ -45,6 +45,10 @@ f1_keywords: - "CS9255" - "CS9256" - "CS9257" + - "CS9258" + - "CS9263" + - "CS9264" + - "CS9266" helpviewer_keywords: - "CS0260" - "CS0261" @@ -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 @@ -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. @@ -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. diff --git a/docs/csharp/language-reference/keywords/field.md b/docs/csharp/language-reference/keywords/field.md new file mode 100644 index 0000000000000..591f3368f1321 --- /dev/null +++ b/docs/csharp/language-reference/keywords/field.md @@ -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)] diff --git a/docs/csharp/language-reference/keywords/get.md b/docs/csharp/language-reference/keywords/get.md index ef716ca5df4ab..fb3e889387a27 100644 --- a/docs/csharp/language-reference/keywords/get.md +++ b/docs/csharp/language-reference/keywords/get.md @@ -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) diff --git a/docs/csharp/language-reference/keywords/index.md b/docs/csharp/language-reference/keywords/index.md index 337a7a408dead..4d5245e3464c7 100644 --- a/docs/csharp/language-reference/keywords/index.md +++ b/docs/csharp/language-reference/keywords/index.md @@ -125,6 +125,7 @@ A contextual keyword is used to provide a specific meaning in the code, but it i [`equals`](equals.md) :::column-end::: :::column::: + [`field`](field.md) [`file`](file.md) [`from`](from-clause.md) [`get`](get.md) diff --git a/docs/csharp/language-reference/keywords/init.md b/docs/csharp/language-reference/keywords/init.md index d77303ed2f801..75c18b1523893 100644 --- a/docs/csharp/language-reference/keywords/init.md +++ b/docs/csharp/language-reference/keywords/init.md @@ -1,6 +1,6 @@ --- -description: "init keyword - C# Reference" -title: "init keyword" +description: "The `init` keyword is used to declare a `set` accessor that can only be called during an object's initialization: either by a constructor or as part of an object initializer." +title: "The init keyword - init only properties" ms.date: 12/06/2023 f1_keywords: - "init" @@ -8,41 +8,36 @@ f1_keywords: helpviewer_keywords: - "init keyword [C#]" --- -# init (C# Reference) +# The `init` keyword (C# Reference) The `init` keyword defines an *accessor* method in a property or indexer. An init-only setter assigns a value to the property or the indexer element **only** during object construction. An `init` enforces immutability, so that once the object is initialized, it can't be changed. An `init` accessor enables calling code to use an [object initializer](../../programming-guide/classes-and-structs/how-to-initialize-objects-by-using-an-object-initializer.md) to set the initial value. As a contrast, an [automatically implemented property](../../programming-guide/classes-and-structs/auto-implemented-properties.md) with only a `get` setter must be initialized by calling a constructor. A property with a `private set` accessor can be modified after construction, but only in the class. -The following example defines both a `get` and an `init` accessor for a property named `YearOfBirth`. It uses a private field named `_yearOfBirth` to back the property value. +The following code demonstrates an `init` accessor in an automatically implemented property: -:::code language="csharp" source="snippets/InitExample1.cs"::: +:::code language="csharp" source="snippets/InitExample2.cs"::: -Often, the `init` accessor consists of a single statement that assigns a value, as it did in the previous example. Because of `init`, the following **doesn't** work: +You might need to implement one of the accessors to provide parameter validation. You can do that using the `field` keyword, introduced as a preview feature in C# 13. The `field` keyword accesses the compiler synthesized backing field for that property. The following example shows a property where the `init` accessor validates the range of the `value` parameter" -```csharp -var john = new Person_InitExample -{ - YearOfBirth = 1984 -}; +:::code language="csharp" source="snippets/InitExample5.cs"::: -john.YearOfBirth = 1926; //Not allowed, as its value can only be set once in the constructor -``` +[!INCLUDE[field-preview](../../includes/field-preview.md)] -An `init` accessor doesn't force callers to set the property. Instead, it allows callers to use an object initializer while prohibiting later modification. You can add the [`required`](required.md) modifier to force callers to set a property. The following example shows an `init` only property with a nullable value type as its backing field. If a caller doesn't initialize the `YearOfBirth` property, that property will have the default `null` value: +The `init` accessor can be used as an expression-bodied member. Example: -:::code language="csharp" source="./snippets/InitNullablityExample.cs" id="Snippet4"::: +:::code language="csharp" source="snippets/InitExample3.cs"::: -To force callers to set an initial non-null value, you add the `required` modifier, as shown in the following example: +The following example defines both a `get` and an `init` accessor for a property named `YearOfBirth`. It uses a private field named `_yearOfBirth` to back the property value. -:::code language="csharp" source="./snippets/InitNullablityExample.cs" id="SnippetNonNullable"::: +:::code language="csharp" source="snippets/InitExample1.cs"::: -The `init` accessor can be used as an expression-bodied member. Example: +An `init` accessor doesn't force callers to set the property. Instead, it allows callers to use an object initializer while prohibiting later modification. You can add the [`required`](required.md) modifier to force callers to set a property. The following example shows an `init` only property with a nullable value type as its backing field. If a caller doesn't initialize the `YearOfBirth` property, that property has the default `null` value: -:::code language="csharp" source="snippets/InitExample3.cs"::: - -The `init` accessor can also be used in autoimplemented properties, as the following example code demonstrates: +:::code language="csharp" source="./snippets/InitNullablityExample.cs" id="Snippet4"::: -:::code language="csharp" source="snippets/InitExample2.cs"::: +To force callers to set an initial non-null value, you add the `required` modifier, as shown in the following example: + +:::code language="csharp" source="./snippets/InitNullablityExample.cs" id="SnippetNonNullable"::: The following example shows the distinction between a `private set`, read only, and `init` property. Both the private set version and the read only version require callers to use the added constructor to set the name property. The `private set` version allows a person to change their name after the instance is constructed. The `init` version doesn't require a constructor. Callers can initialize the properties using an object initializer: @@ -56,5 +51,4 @@ The following example shows the distinction between a `private set`, read only, ## See also -- [C# Keywords](index.md) - [Properties](../../programming-guide/classes-and-structs/properties.md) diff --git a/docs/csharp/language-reference/keywords/set.md b/docs/csharp/language-reference/keywords/set.md index ff8b641d1c666..811e2931856d7 100644 --- a/docs/csharp/language-reference/keywords/set.md +++ b/docs/csharp/language-reference/keywords/set.md @@ -1,31 +1,37 @@ --- description: "The C# set keyword declares a set accessor in a property or indexer. It defines the code to set the value of the property or indexed property." -title: "set keyword: property accessor" -ms.date: 08/15/2024 +title: "The `set` keyword: property accessor" +ms.date: 10/30/2024 f1_keywords: - "set" - "set_CSharpKeyword" helpviewer_keywords: - "set keyword [C#]" --- -# set (C# Reference) +# The set keyword (C# Reference) The `set` keyword defines an *accessor* method in a property or indexer that assigns a value to the property or the indexer element. For more information and examples, see [Properties](../../programming-guide/classes-and-structs/properties.md), [Automatically implemented properties](../../programming-guide/classes-and-structs/auto-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. +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 use automatically implemented properties. The following example implements `Hours` as an automatically implemented property. -:::code language="csharp" source="./snippets/PropertyAccessors.cs" id="GetSetAccessors"::: +:::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. + +You might find that you need to implement one of the accessor bodies. The `field` keyword, added as a preview feature in C# 13 declares a field backed property. 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 to access the compiler synthesized backing field: + +:::code language="csharp" source="./snippets/PropertyAccessors.cs" id="FieldBackedProperty"::: + +[!INCLUDE[field-preview](../../includes/field-preview.md)] Often, the `set` accessor consists of a single statement that assigns a value, as it did in the previous example. You can implement the `set` accessor as an expression-bodied member. The following example implements both the `get` and the `set` accessors as expression-bodied members. :::code language="csharp" source="./snippets/PropertyAccessors.cs" id="GetSetExpressions"::: - -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"::: +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. -> [!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. +:::code language="csharp" source="./snippets/PropertyAccessors.cs" id="GetSetAccessors"::: ## C# language specification @@ -33,5 +39,4 @@ For simple cases in which a property's `get` and `set` accessors perform no othe ## See also -- [C# Keywords](index.md) - [Properties](../../programming-guide/classes-and-structs/properties.md) diff --git a/docs/csharp/language-reference/keywords/snippets/InitExample5.cs b/docs/csharp/language-reference/keywords/snippets/InitExample5.cs new file mode 100644 index 0000000000000..f24d21dcb6b8c --- /dev/null +++ b/docs/csharp/language-reference/keywords/snippets/InitExample5.cs @@ -0,0 +1,13 @@ +class Person_InitExampleFieldProperty +{ + public int YearOfBirth + { + get; + init + { + field = (YearOfBirth <= DateTime.Now.Year) + ? value + : throw new ArgumentOutOfRangeException(nameof(value), "Year of birth can't be in the future"); + } + } +} diff --git a/docs/csharp/language-reference/keywords/snippets/PropertyAccessors.cs b/docs/csharp/language-reference/keywords/snippets/PropertyAccessors.cs index a421646861202..6e5fad6bf1b38 100644 --- a/docs/csharp/language-reference/keywords/snippets/PropertyAccessors.cs +++ b/docs/csharp/language-reference/keywords/snippets/PropertyAccessors.cs @@ -39,3 +39,15 @@ class TimePeriod3 public double Hours { get; set; } } // + +// +class TimePeriod4 +{ + public double Hours { + get; + set => field = (value >= 0) + ? value + : throw new ArgumentOutOfRangeException(nameof(value), "The value must not be negative"); + } +} +// diff --git a/docs/csharp/language-reference/keywords/value.md b/docs/csharp/language-reference/keywords/value.md index b48d82267da39..ea73e704fcaa2 100644 --- a/docs/csharp/language-reference/keywords/value.md +++ b/docs/csharp/language-reference/keywords/value.md @@ -1,25 +1,20 @@ --- -description: "value contextual keyword - C# Reference" -title: "value contextual keyword" -ms.date: 07/20/2015 +description: "The token `value` is an implicit parameter to the `set` accessor for indexers or properties. It represents the parameter to the set accessor." +title: "The `value` implicit parameter" +ms.date: 10/30/2024 f1_keywords: - "value_CSharpKeyword" helpviewer_keywords: - "value keyword [C#]" -ms.assetid: c99d6468-687f-4a46-89b4-a95e1b00bf6d --- -# value (C# Reference) +# The `value` implicit parameter -The contextual keyword `value` is used in the `set` accessor in [property](../../programming-guide/classes-and-structs/properties.md) and [indexer](../../programming-guide/indexers/index.md) declarations. It is similar to an input parameter of a method. The word `value` references the value that client code is attempting to assign to the property or indexer. In the following example, `MyDerivedClass` has a property called `Name` that uses the `value` parameter to assign a new string to the backing field `name`. From the point of view of client code, the operation is written as a simple assignment. +The implicit parameter `value` is used in the `set` accessor in [property](../../programming-guide/classes-and-structs/properties.md) and [indexer](../../programming-guide/indexers/index.md) declarations. It's an input parameter of a method. The word `value` references the value that client code is attempting to assign to the property or indexer. In the following example, `MyDerivedClass` has a property called `Name` that uses the `value` parameter to assign a new string to the backing field `name`. From the point of view of client code, the operation is written as a simple assignment. -[!code-csharp[csrefKeywordsModifiers#26](~/samples/snippets/csharp/VS_Snippets_VBCSharp/csrefKeywordsModifiers/CS/csrefKeywordsModifiers.cs#26)] +:::code language="csharp" source="./snippets/PropertyAccessors.cs" id="GetSetExpressions"::: 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)] - -## See also - -- [C# Keywords](index.md) diff --git a/docs/csharp/language-reference/toc.yml b/docs/csharp/language-reference/toc.yml index 026c7eb5dff7b..864c6b22d0328 100644 --- a/docs/csharp/language-reference/toc.yml +++ b/docs/csharp/language-reference/toc.yml @@ -189,7 +189,9 @@ items: href: ./keywords/set.md - name: when (filter condition) href: ./keywords/when.md - - name: value + - name: field + href: ./keywords/field.md + - name: value (implicit parameter) href: ./keywords/value.md - name: Query Keywords items: @@ -480,7 +482,8 @@ items: partial type, partial member, partial method, partial property, partial indexer, CS0260, CS0261, CS0262, CS0263, CS0264, CS0265, CS0267, CS0282, CS0501, CS0750, CS0751, CS0754, CS0755, CS0756, CS0757, CS0759, CS0761, CS0762, CS0763, CS0764, CS1067, CS8142, CS8663, CS8795, CS8796, CS8797, CS8798, CS8799, CS8800, CS8817, - CS8818, CS8863, CS8988, CS9248, CS9249, CS9250, CS9251, CS9252, CS9253, CS9254, CS9255, CS9256, CS9257 + CS8818, CS8863, CS8988, CS9248, CS9249, CS9250, CS9251, CS9252, CS9253, CS9254, CS9255, CS9256, CS9257, CS9258, CS9263, + CS9264, CS9266 - name: Params modifier href: ./compiler-messages/params-arrays.md displayName: > diff --git a/docs/csharp/linq/get-started/walkthrough-writing-queries-linq.md b/docs/csharp/linq/get-started/walkthrough-writing-queries-linq.md index e8fcec87a2758..ce77df6ed3d0f 100644 --- a/docs/csharp/linq/get-started/walkthrough-writing-queries-linq.md +++ b/docs/csharp/linq/get-started/walkthrough-writing-queries-linq.md @@ -29,7 +29,7 @@ The first step is to create a data source for your queries. The data source for Note the following characteristics: -- The `Student` record consists of autoimplemented properties. +- The `Student` record consists of automatically implemented properties. - Each student in the list is initialized with the primary constructor. - The sequence of scores for each student is initialized with a primary constructor. diff --git a/docs/csharp/programming-guide/classes-and-structs/auto-implemented-properties.md b/docs/csharp/programming-guide/classes-and-structs/auto-implemented-properties.md index 9ce8885985c27..6ff1acc3facad 100644 --- a/docs/csharp/programming-guide/classes-and-structs/auto-implemented-properties.md +++ b/docs/csharp/programming-guide/classes-and-structs/auto-implemented-properties.md @@ -1,7 +1,7 @@ --- title: "Automatically Implemented Properties" description: For an automatically implemented property in C#, the compiler creates a private, anonymous backing field accessed only through get and set accessors of the property. -ms.date: 08/20/2024 +ms.date: 10/30/2024 f1_keywords: - "propertyInitializer_CSharpKeyword" helpviewer_keywords: @@ -12,13 +12,11 @@ helpviewer_keywords: Automatically implemented properties make property-declaration more concise when no other logic is required in the property accessors. They also enable client code to create objects. When you declare a property as shown in the following example, the compiler creates a private, anonymous backing field that can only be accessed through the property's `get` and `set` accessors. `init` accessors can also be declared as automatically implemented properties. -## Example - The following example shows a simple class that has some automatically implemented properties: :::code language="csharp" source="./snippets/properties/AutoImplemented.cs" id="Snippet28"::: -You can't declare automatically implemented properties in interfaces. Automatically implemented properties declare a private instance backing field, and interfaces can't declare instance fields. Declaring a property in an interface without defining a body declares a property with accessors that must be implemented by each type that implements that interface. +You can't declare automatically implemented properties in interfaces. Automatically implemented and field backed properties declare a private instance backing field, and interfaces can't declare instance fields. Declaring a property in an interface without defining a body declares a property with accessors. Each type that implements that interface must implement that property. You can initialize automatically implemented properties similarly to fields: @@ -34,6 +32,25 @@ The class that is shown in the previous example is mutable. Client code can chan For more information, see [How to implement a lightweight class with automatically implemented properties](./how-to-implement-a-lightweight-class-with-auto-implemented-properties.md). +You might need to add validation to an automatically implemented property. C# 13 adds [field backed properties](../../language-reference/keywords/field.md) as a preview feature. You use the `field` keyword to access the compiler synthesized backing field of an automatically implemented property. For example, you could ensure that the `FirstName` property in the preceding example can't be set to `null` or the empty string: + +```csharp +public string FirstName +{ + get; + set + { + field = (string.IsNullOrWhiteSpace(value) is false + ? value + : throw new ArgumentException(nameof(value), "First name can't be whitespace or null")); + } +} = "Jane"; +``` + +This feature enables you to add logic to accessors without requiring you to explicitly declare the backing field. You use the `field` keyword to access the backing field generated by the compiler. + +[!INCLUDE[field-preview](../../includes/field-preview.md)] + ## See also - [Use auto-implemented properties (style rule IDE0032)](../../../fundamentals/code-analysis/style-rules/ide0032.md) diff --git a/docs/csharp/programming-guide/classes-and-structs/object-and-collection-initializers.md b/docs/csharp/programming-guide/classes-and-structs/object-and-collection-initializers.md index 23aa132ff0ed6..5ac7cac4d6595 100644 --- a/docs/csharp/programming-guide/classes-and-structs/object-and-collection-initializers.md +++ b/docs/csharp/programming-guide/classes-and-structs/object-and-collection-initializers.md @@ -12,7 +12,7 @@ C# lets you instantiate an object or collection and perform member assignments i ## Object initializers -Object initializers let you assign values to any accessible fields or properties of an object at creation time without having to invoke a constructor followed by lines of assignment statements. The object initializer syntax enables you to specify arguments for a constructor or omit the arguments (and parentheses syntax). The following example shows how to use an object initializer with a named type, `Cat` and how to invoke the parameterless constructor. Note the use of autoimplemented properties in the `Cat` class. For more information, see [Automatically implemented properties](auto-implemented-properties.md). +Object initializers let you assign values to any accessible fields or properties of an object at creation time without having to invoke a constructor followed by lines of assignment statements. The object initializer syntax enables you to specify arguments for a constructor or omit the arguments (and parentheses syntax). The following example shows how to use an object initializer with a named type, `Cat` and how to invoke the parameterless constructor. Note the use of automatically implemented properties in the `Cat` class. For more information, see [Automatically implemented properties](auto-implemented-properties.md). :::code language="csharp" source="./snippets/object-collection-initializers/BasicObjectInitializers.cs" id="CatDeclaration"::: :::code language="csharp" source="./snippets/object-collection-initializers/BasicObjectInitializers.cs" id="ObjectPropertyInitialization"::: diff --git a/docs/csharp/programming-guide/classes-and-structs/partial-classes-and-methods.md b/docs/csharp/programming-guide/classes-and-structs/partial-classes-and-methods.md index 52f1f8c6aed66..faa81d601e6ac 100644 --- a/docs/csharp/programming-guide/classes-and-structs/partial-classes-and-methods.md +++ b/docs/csharp/programming-guide/classes-and-structs/partial-classes-and-methods.md @@ -1,7 +1,7 @@ --- title: "Partial Classes and Methods" description: Partial classes and methods in C# split the definition of a class, a struct, an interface, or a method over two or more source files. -ms.date: 08/20/2024 +ms.date: 10/31/2024 helpviewer_keywords: - "partial methods [C#]" - "partial classes [C#]" @@ -116,6 +116,14 @@ The method and all calls to the method are removed at compile time when there's Any method that doesn't conform to all those restrictions, including properties and indexers, must provide an implementation. That implementation might be supplied by a *source generator*. [Partial properties](../../language-reference/keywords/partial-member.md) can't be implemented using automatically implemented properties. The compiler can't distinguish between an automatically implemented property, and the declaring declaration of a partial property. +Beginning with C# 13, the implementing declaration for a partial property can use [field backed properties](../../language-reference/keywords/field.md) to define the implementing declaration. A field backed property provides a concise syntax where the `field` keyword accesses the compiler synthesized backing field for the property. For example, you could write the following: + +:::code language="csharp" source="snippets/partial-classes-and-methods/Program.cs" id="FieldProperty"::: + +You can use `field` in either the `get` or `set` accessor, or both. + +[!INCLUDE[field-preview](../../includes/field-preview.md)] + Partial methods enable the implementer of one part of a class to declare a member. The implementer of another part of the class can define that member. There are two scenarios where this separation is useful: templates that generate boilerplate code, and source generators. - **Template code**: The template reserves a method name and signature so that generated code can call the method. These methods follow the restrictions that enable a developer to decide whether to implement the method. If the method isn't implemented, then the compiler removes the method signature and all calls to the method. The calls to the method, including any results that would occur from evaluation of arguments in the calls, have no effect at run time. Therefore, any code in the partial class can freely use a partial method, even if the implementation isn't supplied. No compile-time or run-time errors result if the method is called but not implemented. @@ -140,7 +148,7 @@ partial void OnNameChanged() ## C# Language Specification -For more information, see [Partial types](~/_csharpstandard/standard/classes.md#1527-partial-declarations) and [Partial methods](~/_csharpstandard/standard/classes.md#1569-partial-methods) in the [C# Language Specification](~/_csharpstandard/standard/README.md). The language specification is the definitive source for C# syntax and usage. The additional features for partial methods are defined in the [feature specification](~/_csharplang/proposals/csharp-9.0/extending-partial-methods.md). +For more information, see [Partial types](~/_csharpstandard/standard/classes.md#1527-partial-declarations) and [Partial methods](~/_csharpstandard/standard/classes.md#1569-partial-methods) in the [C# Language Specification](~/_csharpstandard/standard/README.md). The language specification is the definitive source for C# syntax and usage. The new features for partial methods are defined in the [feature specification](~/_csharplang/proposals/csharp-9.0/extending-partial-methods.md). ## See also diff --git a/docs/csharp/programming-guide/classes-and-structs/properties.md b/docs/csharp/programming-guide/classes-and-structs/properties.md index a6a9a2b98764b..03ce87aa5ca30 100644 --- a/docs/csharp/programming-guide/classes-and-structs/properties.md +++ b/docs/csharp/programming-guide/classes-and-structs/properties.md @@ -1,7 +1,7 @@ --- title: "Properties" description: A property in C# is a member that uses accessor methods to read, write, or compute the value of a private field as if it were a public data member. -ms.date: 08/16/2024 +ms.date: 10/30/2024 f1_keywords: - "cs.properties" helpviewer_keywords: @@ -26,30 +26,13 @@ You can initialize a property to a value other than the default by setting a val :::code language="csharp" source="./snippets/properties/Person.cs" id="Initializer"::: -## Access control - -The preceding examples showed read / write properties. You can also create read-only properties, or give different accessibility to the set and get accessors. Suppose that your `Person` class should only enable changing the value of the `FirstName` property from other methods in that class. You could give the set accessor `private` accessibility instead of `public`: - -:::code language="csharp" source="./snippets/properties/Person.cs" id="AccessorModifiers"::: - -The `FirstName` property can be read from any code, but it can be assigned only from code in the `Person` class. - -You can add any restrictive access modifier to either the set or get accessors. An access modifier on an individual accessor must be more restrictive than the access of the property. The preceding code is legal because the `FirstName` property is `public`, but the set accessor is `private`. You couldn't declare a `private` property with a `public` accessor. Property declarations can also be declared `protected`, `internal`, `protected internal`, or, even `private`. - -There are two special access modifiers for `set` accessors: - -- A `set` accessor can have `init` as its access modifier. That `set` accessor can be called only from an object initializer or the type's constructors. It's more restrictive than `private` on the `set` accessor. -- An automatically implemented property can declare a `get` accessor without a `set` accessor. In that case, the compiler allows the `set` accessor to be called only from the type's constructors. It's more restrictive than the `init` accessor on the `set` accessor. +## Field backed properties -Modify the `Person` class so as follows: +In C# 13, you can add validation or other logic in the accessor for a property using the [`field`](../../language-reference/keywords/field.md) keyword preview feature. The `field` keyword accesses the compiler synthesized backing field for a property. It enables you to write a property accessor without explicitly declaring a separate backing field. -:::code language="csharp" source="./snippets/properties/Person.cs" id="Readonly"::: +:::code language="csharp" source="./snippets/properties/Person.cs" id="FieldBackedProperty"::: -The preceding example requires callers to use the constructor that includes the `FirstName` parameter. Callers can't use [object initializers](./object-and-collection-initializers.md) to assign a value to the property. To support initializers, you can make the `set` accessor an `init` accessor, as shown in the following code: - -:::code language="csharp" source="./snippets/properties/Person.cs" id="InitOnly"::: - -These modifiers are often used with the `required` modifier to force proper initialization. +[!INCLUDE[field-preview](../../includes/field-preview.md)] ## Required properties @@ -74,6 +57,31 @@ Read-only properties can implement the `get` accessor as an expression-bodied me The `Name` property is a computed property. There's no backing field for `Name`. The property computes it each time. +## Access control + +The preceding examples showed read / write properties. You can also create read-only properties, or give different accessibility to the set and get accessors. Suppose that your `Person` class should only enable changing the value of the `FirstName` property from other methods in the class. You could give the set accessor `private` accessibility instead of `internal` or `public`: + +:::code language="csharp" source="./snippets/properties/Person.cs" id="AccessorModifiers"::: + +The `FirstName` property can be read from any code, but it can be assigned only from code in the `Person` class. + +You can add any restrictive access modifier to either the set or get accessors. An access modifier on an individual accessor must be more restrictive than the access of the property. The preceding code is legal because the `FirstName` property is `public`, but the set accessor is `private`. You couldn't declare a `private` property with a `public` accessor. Property declarations can also be declared `protected`, `internal`, `protected internal`, or, even `private`. + +There are two special access modifiers for `set` accessors: + +- A `set` accessor can have `init` as its access modifier. That `set` accessor can be called only from an object initializer or the type's constructors. It's more restrictive than `private` on the `set` accessor. +- An automatically implemented property can declare a `get` accessor without a `set` accessor. In that case, the compiler allows the `set` accessor to be called only from the type's constructors. It's more restrictive than the `init` accessor on the `set` accessor. + +Modify the `Person` class so as follows: + +:::code language="csharp" source="./snippets/properties/Person.cs" id="Readonly"::: + +The preceding example requires callers to use the constructor that includes the `FirstName` parameter. Callers can't use [object initializers](./object-and-collection-initializers.md) to assign a value to the property. To support initializers, you can make the `set` accessor an `init` accessor, as shown in the following code: + +:::code language="csharp" source="./snippets/properties/Person.cs" id="InitOnly"::: + +These modifiers are often used with the `required` modifier to force proper initialization. + ## Properties with backing fields You can mix the concept of a computed property with a private field and create a *cached evaluated property*. For example, update the `FullName` property so that the string formatting happens on the first access: diff --git a/docs/csharp/programming-guide/classes-and-structs/restricting-accessor-accessibility.md b/docs/csharp/programming-guide/classes-and-structs/restricting-accessor-accessibility.md index ef533d738fe03..536f3ca3c7e34 100644 --- a/docs/csharp/programming-guide/classes-and-structs/restricting-accessor-accessibility.md +++ b/docs/csharp/programming-guide/classes-and-structs/restricting-accessor-accessibility.md @@ -1,7 +1,7 @@ --- title: "Restricting Accessor Accessibility" description: The get and set accessors of a property in C# have the same visibility or access level by default as property to which they belong. You can restrict access. -ms.date: 07/29/2022 +ms.date: 10/30/2024 helpviewer_keywords: - "read-only properties [C#]" - "read-only indexers [C#]" @@ -9,7 +9,6 @@ helpviewer_keywords: - "properties [C#], read-only" - "asymmetric accessor accessibility [C#]" - "indexers [C#], read-only" -ms.assetid: 6e655798-e112-4301-a680-6310a6e012e1 --- # Restricting Accessor Accessibility (C# Programming Guide) diff --git a/docs/csharp/programming-guide/classes-and-structs/snippets/partial-classes-and-methods/PartialClassesAndMethods.csproj b/docs/csharp/programming-guide/classes-and-structs/snippets/partial-classes-and-methods/PartialClassesAndMethods.csproj index 5493b1b90d80f..62f02481f1bf1 100644 --- a/docs/csharp/programming-guide/classes-and-structs/snippets/partial-classes-and-methods/PartialClassesAndMethods.csproj +++ b/docs/csharp/programming-guide/classes-and-structs/snippets/partial-classes-and-methods/PartialClassesAndMethods.csproj @@ -2,11 +2,12 @@ Exe - net8.0 + net9.0 enable enable PartialClassesAndMethods WrapCoords2.TestCoords + preview diff --git a/docs/csharp/programming-guide/classes-and-structs/snippets/partial-classes-and-methods/Program.cs b/docs/csharp/programming-guide/classes-and-structs/snippets/partial-classes-and-methods/Program.cs index 0f39465b2b163..f7253bc41e0d9 100644 --- a/docs/csharp/programming-guide/classes-and-structs/snippets/partial-classes-and-methods/Program.cs +++ b/docs/csharp/programming-guide/classes-and-structs/snippets/partial-classes-and-methods/Program.cs @@ -165,3 +165,20 @@ partial struct S1 void Struct_Test2() { } } // + +// +// in file1.cs +public partial class PropertyBag +{ + // Defining declaration + public partial int MyProperty { get; set; } +} + +// In file2.cs +public partial class PropertyBag +{ + // Defining declaration + public partial int MyProperty { get => field; set; } +} + +// \ No newline at end of file diff --git a/docs/csharp/programming-guide/classes-and-structs/snippets/properties/Person.cs b/docs/csharp/programming-guide/classes-and-structs/snippets/properties/Person.cs index aa999036f83ca..86d283cf36a15 100644 --- a/docs/csharp/programming-guide/classes-and-structs/snippets/properties/Person.cs +++ b/docs/csharp/programming-guide/classes-and-structs/snippets/properties/Person.cs @@ -24,6 +24,22 @@ public class Person // } +namespace VersionTwoPointFive +{ + // + public class Person + { + public string? FirstName + { + get; + set => field = value.Trim(); + } + + // Omitted for brevity. + } + // +} + namespace VersionThree { // diff --git a/docs/csharp/programming-guide/classes-and-structs/snippets/properties/TimePeriod.cs b/docs/csharp/programming-guide/classes-and-structs/snippets/properties/TimePeriod.cs index 95f67efe1c10c..c9b56001863a5 100644 --- a/docs/csharp/programming-guide/classes-and-structs/snippets/properties/TimePeriod.cs +++ b/docs/csharp/programming-guide/classes-and-structs/snippets/properties/TimePeriod.cs @@ -18,3 +18,20 @@ public int Month } } // + +// +public class DateExample +{ + public int Month + { + get; + set + { + if ((value > 0) && (value < 13)) + { + field = value; + } + } + } +} +// diff --git a/docs/csharp/programming-guide/classes-and-structs/snippets/properties/properties.csproj b/docs/csharp/programming-guide/classes-and-structs/snippets/properties/properties.csproj index 91b464afeacc1..a1bd0d0b2bb22 100644 --- a/docs/csharp/programming-guide/classes-and-structs/snippets/properties/properties.csproj +++ b/docs/csharp/programming-guide/classes-and-structs/snippets/properties/properties.csproj @@ -2,9 +2,10 @@ Exe - net8.0 + net9.0 enable enable + preview diff --git a/docs/csharp/programming-guide/classes-and-structs/using-properties.md b/docs/csharp/programming-guide/classes-and-structs/using-properties.md index 340333b00667e..c71d98fa2c04f 100644 --- a/docs/csharp/programming-guide/classes-and-structs/using-properties.md +++ b/docs/csharp/programming-guide/classes-and-structs/using-properties.md @@ -1,7 +1,7 @@ --- title: "Using Properties" description: These examples illustrate using properties in C#. See how the get and set accessors implement read and write access and find out about uses for properties. -ms.date: 08/20/2024 +ms.date: 10/31/2024 helpviewer_keywords: - "set accessor [C#]" - "get accessor [C#]" @@ -25,6 +25,12 @@ Properties are declared in the class block by specifying the access level of the In this example, `Month` is declared as a property so that the `set` accessor can make sure that the `Month` value is set between 1 and 12. The `Month` property uses a private field to track the actual value. The real location of a property's data is often referred to as the property's "backing store." It's common for properties to use private fields as a backing store. The field is marked private in order to make sure that it can only be changed by calling the property. For more information about public and private access restrictions, see [Access Modifiers](./access-modifiers.md). Automatically implemented properties provide simplified syntax for simple property declarations. For more information, see [Automatically implemented properties](auto-implemented-properties.md). +Beginning with C# 13, you can use [field backed properties](../../language-reference/keywords/field.md) to add validation to the `set` accessor of an automatically implemented property, as shown in the following example: + +:::code language="csharp" source="./snippets/properties/TimePeriod.cs" id="FieldExample"::: + +[!INCLUDE[field-preview](../../includes/field-preview.md)] + ## The get accessor The body of the `get` accessor resembles that of a method. It must return a value of the property type. The C# compiler and Just-in-time (JIT) compiler detect common patterns for implementing the `get` accessor, and optimizes those patterns. For example, a `get` accessor that returns a field without performing any computation is likely optimized to a memory read of that field. Automatically mplemented properties follow this pattern and benefit from these optimizations. However, a virtual `get` accessor method can't be inlined because the compiler doesn't know at compile time which method might actually be called at run time. The following example shows a `get` accessor that returns the value of a private field `_name`: @@ -38,7 +44,8 @@ When you reference the property, except as the target of an assignment, the `get The `get` accessor must be an expression-bodied member, or end in a [return](../../language-reference/statements/jump-statements.md#the-return-statement) or [throw](../../language-reference/statements/exception-handling-statements.md#the-throw-statement) statement, and control can't flow off the accessor body. > [!WARNING] -> It's a bad programming style to change the state of the object by using the `get` accessor. +> +> It's generally a bad programming style to change the state of the object by using the `get` accessor. One exception to this rule is a *lazy evaluated* property, where the value of a property is computed only when it's first accessed. The `get` accessor can be used to return the field value or to compute it and return it. For example: diff --git a/docs/csharp/specification/toc.yml b/docs/csharp/specification/toc.yml index 9f6bb504ed6bb..5d2db2c0cec85 100644 --- a/docs/csharp/specification/toc.yml +++ b/docs/csharp/specification/toc.yml @@ -201,6 +201,8 @@ items: href: ../../../_csharplang/proposals/csharp-9.0/extending-partial-methods.md - name: Partial properties href: ../../../_csharplang/proposals/csharp-13.0/partial-properties.md + - name: Field backed properties (preview) + href: ../../../_csharplang/proposals/field-keyword.md - name: Covariant return types href: ../../../_csharplang/proposals/csharp-9.0/covariant-returns.md - name: Primary constructors diff --git a/docs/csharp/whats-new/csharp-13.md b/docs/csharp/whats-new/csharp-13.md index 4b16bd5532938..dfb4f73a15b26 100644 --- a/docs/csharp/whats-new/csharp-13.md +++ b/docs/csharp/whats-new/csharp-13.md @@ -19,6 +19,8 @@ C# 13 includes the following new features. You can try these features using the - [Partial properties and indexers](#more-partial-members) are now allowed in `partial` types. - [Overload resolution priority](#overload-resolution-priority) allows library authors to designate one overload as better than others. +Beginning with Visual Studio 17.12, C# 13 includes the [`field`](#the-field-keyword) contextual keyword as a preview feature. + C# 13 is supported on **.NET 9**. For more information, see [C# language versioning](../language-reference/configure-language-version.md). You can download the latest .NET 9 preview SDK from the [.NET downloads page](https://dotnet.microsoft.com/download). You can also download [Visual Studio 2022 - preview](https://visualstudio.microsoft.com/vs/), which includes the .NET 9 Preview SDK. @@ -110,7 +112,7 @@ In the same fashion, C# 13 allows `unsafe` contexts in iterator methods. However Before C# 13, `ref struct` types couldn't be declared as the type argument for a generic type or method. Now, generic type declarations can add an anti-constraint, `allows ref struct`. This anti-constraint declares that the type argument supplied for that type parameter can be a `ref struct` type. The compiler enforces ref safety rules on all instances of that type parameter. -For example, you may declare a generic type like the following code: +For example, you might declare a generic type like the following code: ```csharp public class C where T : allows ref struct @@ -127,7 +129,7 @@ This enables types such as a ## `ref struct` interfaces -Before C# 13, `ref struct` types weren't allowed to implement interfaces. Beginning with C# 13, they can. You can declare that a `ref struct` type implements interfaces. However, to ensure ref safety rules, a `ref struct` type can't be converted to an interface type. That is a boxing conversion, and could violate ref safety. From that rule, `ref struct` types can't declare methods that explicitly implement an interface method. Also, `ref struct` types must implement all methods declared in an interface, including those with a default implementation. +Before C# 13, `ref struct` types weren't allowed to implement interfaces. Beginning with C# 13, they can. You can declare that a `ref struct` type implements an interface. However, to ensure ref safety rules, a `ref struct` type can't be converted to an interface type. That conversion is a boxing conversion, and could violate ref safety. From that rule, `ref struct` types can't declare methods that explicitly implement an interface method. Also, `ref struct` types must implement all methods declared in an interface, including those methods with a default implementation. Learn more in the updates on [`ref struct` types](../language-reference/builtin-types/ref-struct.md#restrictions-for-ref-struct-types-that-implement-an-interface). @@ -162,6 +164,16 @@ In C# 13, the compiler recognizes the