From c8673f1590a4d1ae1b956fc8089562d4ef4ba2ba Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 10 Jun 2025 15:57:58 -0400 Subject: [PATCH 01/11] Add speclet Add the speclet to the docset. Add text in What's new in .NET 10 and What's new in C# 14. --- docfx.json | 5 ++++- docs/core/whats-new/dotnet-10/overview.md | 2 ++ docs/csharp/specification/toc.yml | 2 ++ docs/csharp/whats-new/csharp-14.md | 5 +++++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/docfx.json b/docfx.json index 55135a76a5f28..76bfa5afcd650 100644 --- a/docfx.json +++ b/docfx.json @@ -60,7 +60,8 @@ "simple-lambda-parameters-with-modifiers.md", "partial-events-and-constructors.md", "null-conditional-assignment.md", - "extensions.md" + "extensions.md", + "user-defined-compound-assignment.md" ], "src": "_csharplang/proposals", "dest": "csharp/language-reference/proposals", @@ -691,6 +692,7 @@ "_csharplang/proposals/partial-events-and-constructors.md": "Partial events and constructors", "_csharplang/proposals/null-conditional-assignment.md": "Null conditional assignment", "_csharplang/proposals/extensions.md": "Extension members", + "_csharplang/proposals/user-defined-compound-assignment.md": "User-defined compound assignment", "_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", @@ -817,6 +819,7 @@ "_csharplang/proposals/partial-events-and-constructors.md": "This proposal allows partial events and constructors to be declared in partial classes. The event and constructor can be split across class declarations.", "_csharplang/proposals/null-conditional-assignment.md": "This proposal allows the null conditional operator to be used for the destination of assignment expressions. This allows you to assign a value to a property or field only if the left side is not null.", "_csharplang/proposals/extensions.md": "This proposal enables new kinds of extension members. These new extension members support extension properties, extension static members, including extension operators.", + "_csharplang/proposals/user-defined-compound-assignment.md": "This proposal introduces user-defined compound assignment operators. Developers can override compound assignment, increment and decrement operators.", "_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", diff --git a/docs/core/whats-new/dotnet-10/overview.md b/docs/core/whats-new/dotnet-10/overview.md index fe9149e80a94e..ebe9a5c740a1c 100644 --- a/docs/core/whats-new/dotnet-10/overview.md +++ b/docs/core/whats-new/dotnet-10/overview.md @@ -54,6 +54,8 @@ C# 14 introduces several new features and enhancements to improve developer prod - Support for partial instance constructors and partial events, complementing partial methods and properties introduced in C# 13. - New `extension` blocks add support for static extension methods, and static and instance extension properties. - Null-conditional assignment using the `?.` operator. +- User defined compound assignment operators like `+=` and `-=`. +- User defined increment (`++`) and decrement (`--`) operators. For more information, see [What's new in C# 14](../../../csharp/whats-new/csharp-14.md). diff --git a/docs/csharp/specification/toc.yml b/docs/csharp/specification/toc.yml index 3b5d5fba4fb42..a6aa6322f5ee2 100644 --- a/docs/csharp/specification/toc.yml +++ b/docs/csharp/specification/toc.yml @@ -177,6 +177,8 @@ items: href: ../../../_csharplang/proposals/csharp-13.0/overload-resolution-priority.md - name: Null conditional assignment href: ../../../_csharplang/proposals/null-conditional-assignment.md + - name: User defined compound assignment operators + href: ../../../_csharplang/proposals/user-defined-compound-assignment.md - name: Statements items: - name: Global using directive diff --git a/docs/csharp/whats-new/csharp-14.md b/docs/csharp/whats-new/csharp-14.md index e2b817a7ac480..530dc57047d37 100644 --- a/docs/csharp/whats-new/csharp-14.md +++ b/docs/csharp/whats-new/csharp-14.md @@ -15,6 +15,7 @@ C# 14 includes the following new features. You can try these features using the - [Modifiers on simple lambda parameters](#simple-lambda-parameters-with-modifiers) - [`field` backed properties](#the-field-keyword) - [`partial` events and constructors](#more-partial-members) +- [user-defined compound assignment operators](#user-defined-compound-assignment) C# 14 is supported on **.NET 10**. For more information, see [C# language versioning](../language-reference/configure-language-version.md). @@ -136,6 +137,10 @@ Only the implementing declaration of a partial constructor can include a constru The implementing declaration of a partial event must include `add` and `remove` accessors. The defining declaration declares a field-like event. +## User defined compound assignment + +You can learn more in the feature specification for [user-defined compound assignment](~/_csharplang/proposals/user-defined-compound-assignment.md). + ## Null-conditional assignment The null-conditional member access operators, `?.` and `?[]`, can now be used on the left hand side of an assignment or compound assignment. From ba67d4e9897ee3e141f4c9985df1fbf6aa39e8f4 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 10 Jun 2025 17:37:44 -0400 Subject: [PATCH 02/11] Update language reference on operators This commit updates the files for operators where compound assignment can be overloaded. --- .../operators/addition-operator.md | 16 +++--- .../operators/arithmetic-operators.md | 21 ++++---- .../operators/assignment-operator.md | 6 +-- .../operators/bitwise-and-shift-operators.md | 19 +++---- .../operators/boolean-logical-operators.md | 49 ++++++++++--------- .../operators/subtraction-operator.md | 20 ++++---- 6 files changed, 68 insertions(+), 63 deletions(-) diff --git a/docs/csharp/language-reference/operators/addition-operator.md b/docs/csharp/language-reference/operators/addition-operator.md index 6581909a498c1..d5fe4323d9568 100644 --- a/docs/csharp/language-reference/operators/addition-operator.md +++ b/docs/csharp/language-reference/operators/addition-operator.md @@ -1,7 +1,7 @@ --- title: "Addition operators - + and +=" description: "The C# addition operators (`+`, and `+=`) work with operands of numeric, string, or delegate types." -ms.date: 11/22/2024 +ms.date: 06/11/2025 f1_keywords: - "+_CSharpKeyword" - "+=_CSharpKeyword" @@ -16,7 +16,7 @@ helpviewer_keywords: --- # Addition operators - `+` and `+=` -The `+` and `+=` operators are supported by the built-in [integral](../builtin-types/integral-numeric-types.md) and [floating-point](../builtin-types/floating-point-numeric-types.md) numeric types, the [string](../builtin-types/reference-types.md#the-string-type) type, and [delegate](../builtin-types/reference-types.md#the-delegate-type) types. +The built-in [integral](../builtin-types/integral-numeric-types.md) and [floating-point](../builtin-types/floating-point-numeric-types.md) numeric types, the [string](../builtin-types/reference-types.md#the-string-type) type, and [delegate](../builtin-types/reference-types.md#the-delegate-type) types all support the `+` and `+=` operators. For information about the arithmetic `+` operator, see the [Unary plus and minus operators](arithmetic-operators.md#unary-plus-and-minus-operators) and [Addition operator +](arithmetic-operators.md#addition-operator-) sections of the [Arithmetic operators](arithmetic-operators.md) article. @@ -24,11 +24,11 @@ For information about the arithmetic `+` operator, see the [Unary plus and minus When one or both operands are of type [string](../builtin-types/reference-types.md#the-string-type), the `+` operator concatenates the string representations of its operands (the string representation of `null` is an empty string): -[!code-csharp-interactive[string concatenation](snippets/shared/AdditionOperator.cs#AddStrings)] +:::code interactive="try-dotnet-method" language="csharp" source="snippets/shared/AdditionOperator.cs" id="AddStrings"::: [String interpolation](../tokens/interpolated.md) provides a more convenient way to format strings: -[!code-csharp-interactive[string interpolation](snippets/shared/AdditionOperator.cs#UseStringInterpolation)] +:::code interactive="try-dotnet-method" language="csharp" source="snippets/shared/AdditionOperator.cs" id="UseStringInterpolation"::: You can use string interpolation to initialize a constant string when all the expressions used for placeholders are also constant strings. @@ -38,7 +38,7 @@ Beginning with C# 11, the `+` operator performs string concatenation for UTF-8 l For operands of the same [delegate](../builtin-types/reference-types.md#the-delegate-type) type, the `+` operator returns a new delegate instance that, when invoked, invokes the left-hand operand and then invokes the right-hand operand. If any of the operands is `null`, the `+` operator returns the value of another operand (which also might be `null`). The following example shows how delegates can be combined with the `+` operator: -[!code-csharp-interactive[delegate combination](snippets/shared/AdditionOperator.cs#AddDelegates)] +:::code interactive="try-dotnet-method" language="csharp" source="snippets/shared/AdditionOperator.cs" id="AddDelegates"::: To perform delegate removal, use the [`-` operator](subtraction-operator.md#delegate-removal). @@ -62,17 +62,17 @@ Except that `x` is only evaluated once. The following example demonstrates the usage of the `+=` operator: -[!code-csharp-interactive[+= examples](snippets/shared/AdditionOperator.cs#AddAndAssign)] +:::code interactive="try-dotnet-method" language="csharp" source="snippets/shared/AdditionOperator.cs" id="AddAndAssign"::: You also use the `+=` operator to specify an event handler method when you subscribe to an [event](../keywords/event.md). For more information, see [How to: subscribe to and unsubscribe from events](../../programming-guide/events/how-to-subscribe-to-and-unsubscribe-from-events.md). ## Operator overloadability -A user-defined type can [overload](operator-overloading.md) the `+` operator. When a binary `+` operator is overloaded, the `+=` operator is also implicitly overloaded. A user-defined type can't explicitly overload the `+=` operator. +A user-defined type can [overload](operator-overloading.md) the `+` operator. When a binary `+` operator is overloaded, the `+=` operator is also implicitly overloaded. Beginning with C# 14, a user-defined type can explicitly overload the `+=` operator to provide a more efficient implementation. Typically, a type overloads the `+=` operator because the value can be updated in place, rather than allocating performing the addition and return a new instance. If a type doesn't provide an explicit overload, the compiler generates the implicit overload. ## C# language specification -For more information, see the [Unary plus operator](~/_csharpstandard/standard/expressions.md#1292-unary-plus-operator) and [Addition operator](~/_csharpstandard/standard/expressions.md#12105-addition-operator) sections of the [C# language specification](~/_csharpstandard/standard/README.md). +For more information, see the [Unary plus operator](~/_csharpstandard/standard/expressions.md#1292-unary-plus-operator) and [Addition operator](~/_csharpstandard/standard/expressions.md#12105-addition-operator) sections of the [C# language specification](~/_csharpstandard/standard/README.md). For more information on overloading the compound assignment operators in C# 14 and later, see the [user defined compound assignment](~/_csharplang/proposals/user-defined-compound-assignment.md) feature specification. ## See also diff --git a/docs/csharp/language-reference/operators/arithmetic-operators.md b/docs/csharp/language-reference/operators/arithmetic-operators.md index a581621cff764..d792d475e3d17 100644 --- a/docs/csharp/language-reference/operators/arithmetic-operators.md +++ b/docs/csharp/language-reference/operators/arithmetic-operators.md @@ -1,7 +1,7 @@ --- title: "Arithmetic operators" description: "Learn about C# operators that perform multiplication, division, remainder, addition, and subtraction operations with numeric types." -ms.date: 07/25/2022 +ms.date: 06/11/2025 author: pkulikov f1_keywords: - "++_CSharpKeyword" @@ -38,9 +38,9 @@ The following operators perform arithmetic operations with operands of numeric t - Unary [`++` (increment)](#increment-operator-), [`--` (decrement)](#decrement-operator---), [`+` (plus)](#unary-plus-and-minus-operators), and [`-` (minus)](#unary-plus-and-minus-operators) operators - Binary [`*` (multiplication)](#multiplication-operator-), [`/` (division)](#division-operator-), [`%` (remainder)](#remainder-operator-), [`+` (addition)](#addition-operator-), and [`-` (subtraction)](#subtraction-operator--) operators -Those operators are supported by all [integral](../builtin-types/integral-numeric-types.md) and [floating-point](../builtin-types/floating-point-numeric-types.md) numeric types. +All [integral](../builtin-types/integral-numeric-types.md) and [floating-point](../builtin-types/floating-point-numeric-types.md) numeric types support these operators. -In the case of integral types, those operators (except the `++` and `--` operators) are defined for the `int`, `uint`, `long`, and `ulong` types. When operands are of other integral types (`sbyte`, `byte`, `short`, `ushort`, or `char`), their values are converted to the `int` type, which is also the result type of an operation. When operands are of different integral or floating-point types, their values are converted to the closest containing type, if such a type exists. For more information, see the [Numeric promotions](~/_csharpstandard/standard/expressions.md#1247-numeric-promotions) section of the [C# language specification](~/_csharpstandard/standard/README.md). The `++` and `--` operators are defined for all integral and floating-point numeric types and the [char](../builtin-types/char.md) type. The result type of a [compound assignment expression](#compound-assignment) is the type of the left-hand operand. +The `int`, `uint`, `long`, and `ulong` types define all these operators. The other integral types (`sbyte`, `byte`, `short`, `ushort`, or `char`) define only the `++` and `--` operators. For other operators, When operands are of the integral types `sbyte`, `byte`, `short`, `ushort`, or `char`, their values are converted to the `int` type and the result type is `int`. When operands are of different integral or floating-point types, their values are converted to the closest containing type, if such a type exists. For more information, see the [Numeric promotions](~/_csharpstandard/standard/expressions.md#1247-numeric-promotions) section of the [C# language specification](~/_csharpstandard/standard/README.md). The `++` and `--` operators are defined for all integral and floating-point numeric types and the [char](../builtin-types/char.md) type. The result type of a [compound assignment expression](#compound-assignment) is the type of the left-hand operand. ## Increment operator ++ @@ -114,7 +114,7 @@ For the `float`, `double`, and `decimal` types, the result of the `/` operator i :::code language="csharp" interactive="try-dotnet-method" source="snippets/shared/ArithmeticOperators.cs" id="FloatingPointDivision"::: -If one of the operands is `decimal`, another operand can be neither `float` nor `double`, because neither `float` nor `double` is implicitly convertible to `decimal`. You must explicitly convert the `float` or `double` operand to the `decimal` type. For more information about conversions between numeric types, see [Built-in numeric conversions](../builtin-types/numeric-conversions.md). +If one of the operands is `decimal`, another operand can't be `float` nor `double`, because neither `float` nor `double` is implicitly convertible to `decimal`. You must explicitly convert the `float` or `double` operand to the `decimal` type. For more information about conversions between numeric types, see [Built-in numeric conversions](../builtin-types/numeric-conversions.md). ## Remainder operator % @@ -136,7 +136,7 @@ For the `float` and `double` operands, the result of `x % y` for the finite `x` - The absolute value of `z` is the value produced by `|x| - n * |y|` where `n` is the largest possible integer that is less than or equal to `|x| / |y|` and `|x|` and `|y|` are the absolute values of `x` and `y`, respectively. > [!NOTE] -> This method of computing the remainder is analogous to that used for integer operands, but different from the IEEE 754 specification. If you need the remainder operation that complies with the IEEE 754 specification, use the method. +> This method of computing the remainder is analogous to the method used for integer operands, but different from the IEEE 754 specification. If you need the remainder operation that complies with the IEEE 754 specification, use the method. For information about the behavior of the `%` operator with non-finite operands, see the [Remainder operator](~/_csharpstandard/standard/expressions.md#12104-remainder-operator) section of the [C# language specification](~/_csharpstandard/standard/README.md). @@ -170,13 +170,13 @@ For a binary operator `op`, a compound assignment expression of the form x op= y ``` -is equivalent to +Is equivalent to ```csharp x = x op y ``` -except that `x` is only evaluated once. +Except that `x` is only evaluated once. The following example demonstrates the usage of compound assignment with arithmetic operators: @@ -247,7 +247,7 @@ For more information, see remarks at the [System.Double](/dotnet/api/system.doub ## Operator overloadability -A user-defined type can [overload](operator-overloading.md) the unary (`++`, `--`, `+`, and `-`) and binary (`*`, `/`, `%`, `+`, and `-`) arithmetic operators. When a binary operator is overloaded, the corresponding compound assignment operator is also implicitly overloaded. A user-defined type can't explicitly overload a compound assignment operator. +A user-defined type can [overload](operator-overloading.md) the unary (`++`, `--`, `+`, and `-`) and binary (`*`, `/`, `%`, `+`, and `-`) arithmetic operators. When a binary operator is overloaded, the corresponding compound assignment operator is also implicitly overloaded. Beginning with C# 14, a user-defined type can explicitly overload the compound assignment operators (`op=`) to provide a more efficient implementation. Typically, a type overloads these operators because the value can be updated in place, rather than allocating performing the addition and return a new instance. If a type doesn't provide an explicit overload, the compiler generates the implicit overload. ### User-defined checked operators @@ -268,10 +268,11 @@ You can use the `checked` modifier only when you overload any of the following o - Unary `++`, `--`, and `-` operators - Binary `*`, `/`, `+`, and `-` operators +- Compound assignment `*=`, `/=`, `+=`, and `-=` operators (C# 14 and later) - [Explicit conversion operators](user-defined-conversion-operators.md) > [!NOTE] -> The overflow-checking context within the body of a checked operator is not affected by the presence of the `checked` modifier. The default context is defined by the value of the [**CheckForOverflowUnderflow**](../compiler-options/language.md#checkforoverflowunderflow) compiler option. Use the [`checked` and `unchecked` statements](../statements/checked-and-unchecked.md) to explicitly specify the overflow-checking context, as the example at the beginning of this section demonstrates. +> The overflow-checking context within the body of a checked operator isn't affected by the presence of the `checked` modifier. The default context is defined by the value of the [**CheckForOverflowUnderflow**](../compiler-options/language.md#checkforoverflowunderflow) compiler option. Use the [`checked` and `unchecked` statements](../statements/checked-and-unchecked.md) to explicitly specify the overflow-checking context, as the example at the beginning of this section demonstrates. ## C# language specification @@ -289,6 +290,8 @@ For more information, see the following sections of the [C# language specificati - [Compound assignment](~/_csharpstandard/standard/expressions.md#12214-compound-assignment) - [The checked and unchecked operators](~/_csharpstandard/standard/expressions.md#12820-the-checked-and-unchecked-operators) - [Numeric promotions](~/_csharpstandard/standard/expressions.md#1247-numeric-promotions) +- [User defined compound assignment](~/_csharplang/proposals/user-defined-compound-assignment.md) + ## See also diff --git a/docs/csharp/language-reference/operators/assignment-operator.md b/docs/csharp/language-reference/operators/assignment-operator.md index 86471b7b47e28..4a178f5a04d55 100644 --- a/docs/csharp/language-reference/operators/assignment-operator.md +++ b/docs/csharp/language-reference/operators/assignment-operator.md @@ -1,7 +1,7 @@ --- title: "Assignment operators - assign an expression to a variable" description: "C# Assignment sets the value of the expression. Alternatively, `ref` assignment sets the reference of a reference variable." -ms.date: 04/04/2025 +ms.date: 06/11/2025 f1_keywords: - "=_CSharpKeyword" helpviewer_keywords: @@ -77,11 +77,11 @@ You can use the null-coalescing assignment operator `??=` to assign the value of A user-defined type can't [overload](operator-overloading.md) the assignment operator. However, a user-defined type can define an implicit conversion to another type. That way, the value of a user-defined type can be assigned to a variable, a property, or an indexer element of another type. For more information, see [User-defined conversion operators](user-defined-conversion-operators.md). -A user-defined type can't explicitly overload a compound assignment operator. However, if a user-defined type overloads a binary operator `op`, the `op=` operator, if it exists, is also implicitly overloaded. +If a user-defined type overloads a binary operator `op`, the `op=` operator, if it exists, is also implicitly overloaded. Beginning with C# 14, a user-defined type can explicitly overload the compound assignment operators (`op=`) to provide a more efficient implementation. Typically, a type overloads these operators because the value can be updated in place, rather than allocating performing the addition and return a new instance. If a type doesn't provide an explicit overload, the compiler generates the implicit overload. ## C# language specification -For more information, see the [Assignment operators](~/_csharpstandard/standard/expressions.md#1221-assignment-operators) section of the [C# language specification](~/_csharpstandard/standard/README.md). +For more information, see the [Assignment operators](~/_csharpstandard/standard/expressions.md#1221-assignment-operators) section of the [C# language specification](~/_csharpstandard/standard/README.md) and the [User defined compound assignment](~/_csharplang/proposals/user-defined-compound-assignment.md) feature specification. ## See also diff --git a/docs/csharp/language-reference/operators/bitwise-and-shift-operators.md b/docs/csharp/language-reference/operators/bitwise-and-shift-operators.md index 9fbef850bb76a..96c6c1658817a 100644 --- a/docs/csharp/language-reference/operators/bitwise-and-shift-operators.md +++ b/docs/csharp/language-reference/operators/bitwise-and-shift-operators.md @@ -1,7 +1,7 @@ --- title: "Bitwise and shift operators - perform boolean (AND, NOT, OR, XOR) and shift operations on individual bits in integral types" -description: "Learn about C# operators that perform bitwise logical (AND - `&`, NOT - `~`, OR - `|`, XOR - `^`) or shift operations( `<<`, and `>>`) with operands of integral types. " -ms.date: 11/29/2022 +description: "Learn about C# operators that perform bitwise logical (AND - `&`, NOT - `~`, OR - `|`, XOR - `^`) or shift operations(`<<`, and `>>`) with operands of integral types. " +ms.date: 06/11/2025 author: pkulikov f1_keywords: - "~_CSharpKeyword" @@ -89,7 +89,7 @@ The high-order empty bit positions are set based on the type of the left-hand op :::code language="csharp" interactive="try-dotnet-method" source="snippets/shared/BitwiseAndShiftOperators.cs" id="LogicalRightShift"::: > [!NOTE] -> Use the [unsigned right-shift operator](#unsigned-right-shift-operator-) to perform a *logical* shift on operands of signed integer types. This is preferred to casting a left-hand operand to an unsigned type and then casting the result of a shift operation back to a signed type. +> Use the [unsigned right-shift operator](#unsigned-right-shift-operator-) to perform a *logical* shift on operands of signed integer types. The logical shift is preferred to casting a left-hand operand to an unsigned type and then casting the result of a shift operation back to a signed type. ## Unsigned right-shift operator >>> @@ -131,13 +131,13 @@ For a binary operator `op`, a compound assignment expression of the form x op= y ``` -is equivalent to +Is equivalent to ```csharp x = x op y ``` -except that `x` is only evaluated once. +Except that `x` is only evaluated once. The following example demonstrates the usage of compound assignment with bitwise and shift operators: @@ -167,9 +167,9 @@ For the complete list of C# operators ordered by precedence level, see the [Oper For the `x << count`, `x >> count`, and `x >>> count` expressions, the actual shift count depends on the type of `x` as follows: -- If the type of `x` is `int` or `uint`, the shift count is defined by the low-order *five* bits of the right-hand operand. That is, the shift count is computed from `count & 0x1F` (or `count & 0b_1_1111`). +- If the type of `x` is `int` or `uint`, the low-order *five* bits of the right-hand operand define the shift count. That is, the shift count is computed from `count & 0x1F` (or `count & 0b_1_1111`). -- If the type of `x` is `long` or `ulong`, the shift count is defined by the low-order *six* bits of the right-hand operand. That is, the shift count is computed from `count & 0x3F` (or `count & 0b_11_1111`). +- If the type of `x` is `long` or `ulong`, the low-order *six* bits of the right-hand operand define the shift count. That is, the shift count is computed from `count & 0x3F` (or `count & 0b_11_1111`). The following example demonstrates that behavior: @@ -180,13 +180,13 @@ The following example demonstrates that behavior: ## Enumeration logical operators -The `~`, `&`, `|`, and `^` operators are also supported by any [enumeration](../builtin-types/enum.md) type. For operands of the same enumeration type, a logical operation is performed on the corresponding values of the underlying integral type. For example, for any `x` and `y` of an enumeration type `T` with an underlying type `U`, the `x & y` expression produces the same result as the `(T)((U)x & (U)y)` expression. +Every [enumeration](../builtin-types/enum.md) type supports the `~`, `&`, `|`, and `^` operators. For operands of the same enumeration type, a logical operation is performed on the corresponding values of the underlying integral type. For example, for any `x` and `y` of an enumeration type `T` with an underlying type `U`, the `x & y` expression produces the same result as the `(T)((U)x & (U)y)` expression. You typically use bitwise logical operators with an enumeration type that is defined with the [Flags](xref:System.FlagsAttribute) attribute. For more information, see the [Enumeration types as bit flags](../builtin-types/enum.md#enumeration-types-as-bit-flags) section of the [Enumeration types](../builtin-types/enum.md) article. ## Operator overloadability -A user-defined type can [overload](operator-overloading.md) the `~`, `<<`, `>>`, `>>>`, `&`, `|`, and `^` operators. When a binary operator is overloaded, the corresponding compound assignment operator is also implicitly overloaded. A user-defined type can't explicitly overload a compound assignment operator. +A user-defined type can [overload](operator-overloading.md) the `~`, `<<`, `>>`, `>>>`, `&`, `|`, and `^` operators. When a binary operator is overloaded, the corresponding compound assignment operator is also implicitly overloaded. Beginning with C# 14, a user-defined type can explicitly overload the compound assignment operators to provide a more efficient implementation. Typically, a type overloads these operators because the value can be updated in place, rather than allocating performing the addition and return a new instance. If a type doesn't provide an explicit overload, the compiler generates the implicit overload. If a user-defined type `T` overloads the `<<`, `>>`, or `>>>` operator, the type of the left-hand operand must be `T`. In C# 10 and earlier, the type of the right-hand operand must be `int`; beginning with C# 11, the type of the right-hand operand of an overloaded shift operator can be any. @@ -201,6 +201,7 @@ For more information, see the following sections of the [C# language specificati - [Numeric promotions](~/_csharpstandard/standard/expressions.md#1247-numeric-promotions) - [C# 11 - Relaxed shift requirements](~/_csharplang/proposals/csharp-11.0/relaxing_shift_operator_requirements.md) - [C# 11 - Logical right-shift operator](~/_csharplang/proposals/csharp-11.0/unsigned-right-shift-operator.md) +- [User defined compound assignment](~/_csharplang/proposals/user-defined-compound-assignment.md) ## See also diff --git a/docs/csharp/language-reference/operators/boolean-logical-operators.md b/docs/csharp/language-reference/operators/boolean-logical-operators.md index 3c4a0031a18d6..efbe493a4639c 100644 --- a/docs/csharp/language-reference/operators/boolean-logical-operators.md +++ b/docs/csharp/language-reference/operators/boolean-logical-operators.md @@ -1,7 +1,7 @@ --- title: "Boolean logical operators - the boolean and, or, not, and xor operators" description: "C# logical operators perform logical negation (`!`), conjunction (AND - `&`, `&&`), and inclusive and exclusive disjunction (OR - `|`, `||`, `^`) operations with Boolean operands." -ms.date: 11/29/2022 +ms.date: 06/10/2025 author: pkulikov f1_keywords: - "!_CSharpKeyword" @@ -50,7 +50,7 @@ For operands of the [integral numeric types](../builtin-types/integral-numeric-t The unary prefix `!` operator computes logical negation of its operand. That is, it produces `true`, if the operand evaluates to `false`, and `false`, if the operand evaluates to `true`: -[!code-csharp-interactive[logical negation](snippets/shared/BooleanLogicalOperators.cs#Negation)] +:::code interactive="try-dotnet-method" language="csharp" source="snippets/shared/BooleanLogicalOperators.cs" id="Negation"::: The unary postfix `!` operator is the [null-forgiving operator](null-forgiving.md). @@ -62,7 +62,7 @@ The `&` operator always evaluates both operands. When the left-hand operand eval In the following example, the right-hand operand of the `&` operator is a method call, which is performed regardless of the value of the left-hand operand: -[!code-csharp-interactive[logical AND](snippets/shared/BooleanLogicalOperators.cs#And)] +:::code interactive="try-dotnet-method" language="csharp" source="snippets/shared/BooleanLogicalOperators.cs" id="And"::: The [conditional logical AND operator](#conditional-logical-and-operator-) `&&` also computes the logical AND of its operands, but doesn't evaluate the right-hand operand if the left-hand operand evaluates to `false`. @@ -72,7 +72,7 @@ For operands of the [integral numeric types](../builtin-types/integral-numeric-t The `^` operator computes the logical exclusive OR, also known as the logical XOR, of its operands. The result of `x ^ y` is `true` if `x` evaluates to `true` and `y` evaluates to `false`, or `x` evaluates to `false` and `y` evaluates to `true`. Otherwise, the result is `false`. That is, for the `bool` operands, the `^` operator computes the same result as the [inequality operator](equality-operators.md#inequality-operator-) `!=`. -[!code-csharp-interactive[logical exclusive OR](snippets/shared/BooleanLogicalOperators.cs#Xor)] +:::code interactive="try-dotnet-method" language="csharp" source="snippets/shared/BooleanLogicalOperators.cs" id="Xor"::: For operands of the [integral numeric types](../builtin-types/integral-numeric-types.md), the `^` operator computes the [bitwise logical exclusive OR](bitwise-and-shift-operators.md#logical-exclusive-or-operator-) of its operands. @@ -84,7 +84,7 @@ The `|` operator always evaluates both operands. When the left-hand operand eval In the following example, the right-hand operand of the `|` operator is a method call, which is performed regardless of the value of the left-hand operand: -[!code-csharp-interactive[logical OR](snippets/shared/BooleanLogicalOperators.cs#Or)] +:::code interactive="try-dotnet-method" language="csharp" source="snippets/shared/BooleanLogicalOperators.cs" id="Or"::: The [conditional logical OR operator](#conditional-logical-or-operator-) `||` also computes the logical OR of its operands, but doesn't evaluate the right-hand operand if the left-hand operand evaluates to `true`. @@ -96,7 +96,7 @@ The conditional logical AND operator `&&`, also known as the "short-circuiting" In the following example, the right-hand operand of the `&&` operator is a method call, which isn't performed if the left-hand operand evaluates to `false`: -[!code-csharp-interactive[conditional logical AND](snippets/shared/BooleanLogicalOperators.cs#ConditionalAnd)] +:::code interactive="try-dotnet-method" language="csharp" source="snippets/shared/BooleanLogicalOperators.cs" id="ConditionalAnd"::: The [logical AND operator](#logical-and-operator-) `&` also computes the logical AND of its operands, but always evaluates both operands. @@ -106,7 +106,7 @@ The conditional logical OR operator `||`, also known as the "short-circuiting" l In the following example, the right-hand operand of the `||` operator is a method call, which isn't performed if the left-hand operand evaluates to `true`: -[!code-csharp-interactive[conditional logical OR](snippets/shared/BooleanLogicalOperators.cs#ConditionalOr)] +:::code interactive="try-dotnet-method" language="csharp" source="snippets/shared/BooleanLogicalOperators.cs" id="ConditionalOr"::: The [logical OR operator](#logical-or-operator-) `|` also computes the logical OR of its operands, but always evaluates both operands. @@ -120,23 +120,23 @@ For `bool?` operands, the [`&` (logical AND)](#logical-and-operator-) and [`|` ( The following table presents that semantics: -|x|y|x&y|x|y| -|----|----|----|----| -|true|true|true|true| -|true|false|false|true| -|true|null|null|true| -|false|true|false|true| -|false|false|false|false| -|false|null|false|null| -|null|true|null|true| -|null|false|false|null| -|null|null|null|null| +| x | y | x & y | x \| y | +|-------|-------|-------|----------| +| true | true | true | true | +| true | false | false | true | +| true | null | null | true | +| false | true | false | true | +| false | false | false | false | +| false | null | false | null | +| null | true | null | true | +| null | false | false | null | +| null | null | null | null | The behavior of those operators differs from the typical operator behavior with nullable value types. Typically, an operator that is defined for operands of a value type can be also used with operands of the corresponding nullable value type. Such an operator produces `null` if any of its operands evaluates to `null`. However, the `&` and `|` operators can produce non-null even if one of the operands evaluates to `null`. For more information about the operator behavior with nullable value types, see the [Lifted operators](../builtin-types/nullable-value-types.md#lifted-operators) section of the [Nullable value types](../builtin-types/nullable-value-types.md) article. You can also use the `!` and `^` operators with `bool?` operands, as the following example shows: -[!code-csharp-interactive[lifted negation and xor](snippets/shared/BooleanLogicalOperators.cs#WithNullableBoolean)] +:::code interactive="try-dotnet-method" language="csharp" source="snippets/shared/BooleanLogicalOperators.cs" id="WithNullableBoolean"::: The conditional logical operators `&&` and `||` don't support `bool?` operands. @@ -148,17 +148,17 @@ For a binary operator `op`, a compound assignment expression of the form x op= y ``` -is equivalent to +Is equivalent to ```csharp x = x op y ``` -except that `x` is only evaluated once. +Except that `x` is only evaluated once. The `&`, `|`, and `^` operators support compound assignment, as the following example shows: -[!code-csharp-interactive[compound assignment](snippets/shared/BooleanLogicalOperators.cs#CompoundAssignment)] +:::code interactive="try-dotnet-method" language="csharp" source="snippets/shared/BooleanLogicalOperators.cs" id="CompoundAssignment"::: > [!NOTE] > The conditional logical operators `&&` and `||` don't support compound assignment. @@ -176,13 +176,13 @@ The following list orders logical operators starting from the highest precedence Use parentheses, `()`, to change the order of evaluation imposed by operator precedence: -[!code-csharp-interactive[operator precedence](snippets/shared/BooleanLogicalOperators.cs#Precedence)] +:::code interactive="try-dotnet-method" language="csharp" source="snippets/shared/BooleanLogicalOperators.cs" id="Precedence"::: For the complete list of C# operators ordered by precedence level, see the [Operator precedence](index.md#operator-precedence) section of the [C# operators](index.md) article. ## Operator overloadability -A user-defined type can [overload](operator-overloading.md) the `!`, `&`, `|`, and `^` operators. When a binary operator is overloaded, the corresponding compound assignment operator is also implicitly overloaded. A user-defined type can't explicitly overload a compound assignment operator. +A user-defined type can [overload](operator-overloading.md) the `!`, `&`, `|`, and `^` operators. When a binary operator is overloaded, the corresponding compound assignment operator is also implicitly overloaded. Beginning with C# 14, a user-defined type can explicitly overload the compound assignment operators to provide a more efficient implementation. Typically, a type overloads these operators because the value can be updated in place, rather than allocating performing the addition and return a new instance. If a type doesn't provide an explicit overload, the compiler generates the implicit overload. A user-defined type can't overload the conditional logical operators `&&` and `||`. However, if a user-defined type overloads the [true and false operators](true-false-operators.md) and the `&` or `|` operator in a certain way, the `&&` or `||` operation, respectively, can be evaluated for the operands of that type. For more information, see the [User-defined conditional logical operators](~/_csharpstandard/standard/expressions.md#12143-user-defined-conditional-logical-operators) section of the [C# language specification](~/_csharpstandard/standard/README.md). @@ -194,6 +194,7 @@ For more information, see the following sections of the [C# language specificati - [Logical operators](~/_csharpstandard/standard/expressions.md#1213-logical-operators) - [Conditional logical operators](~/_csharpstandard/standard/expressions.md#1214-conditional-logical-operators) - [Compound assignment](~/_csharpstandard/standard/expressions.md#12214-compound-assignment) +- [User defined compound assignment](~/_csharplang/proposals/user-defined-compound-assignment.md) ## See also diff --git a/docs/csharp/language-reference/operators/subtraction-operator.md b/docs/csharp/language-reference/operators/subtraction-operator.md index 305e9357d1f9c..2492adb1f619b 100644 --- a/docs/csharp/language-reference/operators/subtraction-operator.md +++ b/docs/csharp/language-reference/operators/subtraction-operator.md @@ -1,7 +1,7 @@ --- title: "- and -= operators - subtraction (minus) operators" description: "Learn about the C# subtraction (minus) operator and how it works with operands of numeric or delegate types." -ms.date: 11/28/2022 +ms.date: 06/11/2025 f1_keywords: - "-_CSharpKeyword" - "-=_CSharpKeyword" @@ -15,7 +15,7 @@ helpviewer_keywords: --- # - and -= operators - subtraction (minus) -The `-` and `-=` operators are supported by the built-in [integral](../builtin-types/integral-numeric-types.md) and [floating-point](../builtin-types/floating-point-numeric-types.md) numeric types and [delegate](../builtin-types/reference-types.md#the-delegate-type) types. +The built-in [integral](../builtin-types/integral-numeric-types.md) and [floating-point](../builtin-types/floating-point-numeric-types.md) numeric types and [delegate](../builtin-types/reference-types.md#the-delegate-type) types all support the `-` and `-=` operators. For information about the arithmetic `-` operator, see the [Unary plus and minus operators](arithmetic-operators.md#unary-plus-and-minus-operators) and [Subtraction operator -](arithmetic-operators.md#subtraction-operator--) sections of the [Arithmetic operators](arithmetic-operators.md) article. @@ -25,17 +25,17 @@ For operands of the same [delegate](../builtin-types/reference-types.md#the-dele - If both operands are non-null and the invocation list of the right-hand operand is a proper contiguous sublist of the invocation list of the left-hand operand, the result of the operation is a new invocation list that is obtained by removing the right-hand operand's entries from the invocation list of the left-hand operand. If the right-hand operand's list matches multiple contiguous sublists in the left-hand operand's list, only the right-most matching sublist is removed. If removal results in an empty list, the result is `null`. - [!code-csharp-interactive[delegate removal](snippets/shared/SubtractionOperator.cs#DelegateRemoval)] + :::code interactive="try-dotnet-method" language="csharp" source="snippets/shared/SubtractionOperator.cs" id="DelegateRemoval"::: - If the invocation list of the right-hand operand isn't a proper contiguous sublist of the invocation list of the left-hand operand, the result of the operation is the left-hand operand. For example, removing a delegate that isn't part of the multicast delegate does nothing and results in the unchanged multicast delegate. - [!code-csharp-interactive[delegate removal with no effect](snippets/shared/SubtractionOperator.cs#DelegateRemovalNoChange)] + :::code interactive="try-dotnet-method" language="csharp" source="snippets/shared/SubtractionOperator.cs" id="DelegateRemovalNoChange"::: The preceding example also demonstrates that during delegate removal delegate instances are compared. For example, delegates that are produced from evaluation of identical [lambda expressions](lambda-expressions.md) aren't equal. For more information about delegate equality, see the [Delegate equality operators](~/_csharpstandard/standard/expressions.md#12129-delegate-equality-operators) section of the [C# language specification](~/_csharpstandard/standard/README.md). - If the left-hand operand is `null`, the result of the operation is `null`. If the right-hand operand is `null`, the result of the operation is the left-hand operand. - [!code-csharp-interactive[delegate removal and null](snippets/shared/SubtractionOperator.cs#DelegateRemovalAndNull)] + :::code interactive="try-dotnet-method" language="csharp" source="snippets/shared/SubtractionOperator.cs" id="DelegateRemovalAndNull"::: To combine delegates, use the [`+` operator](addition-operator.md#delegate-combination). @@ -49,27 +49,27 @@ An expression using the `-=` operator, such as x -= y ``` -is equivalent to +Is equivalent to ```csharp x = x - y ``` -except that `x` is only evaluated once. +Except that `x` is only evaluated once. The following example demonstrates the usage of the `-=` operator: -[!code-csharp-interactive[-= examples](snippets/shared/SubtractionOperator.cs#SubtractAndAssign)] +:::code interactive="try-dotnet-method" language="csharp" source="snippets/shared/SubtractionOperator.cs" id="SubtractAndAssign"::: You also use the `-=` operator to specify an event handler method to remove when you unsubscribe from an [event](../keywords/event.md). For more information, see [How to subscribe to and unsubscribe from events](../../programming-guide/events/how-to-subscribe-to-and-unsubscribe-from-events.md). ## Operator overloadability -A user-defined type can [overload](operator-overloading.md) the `-` operator. When a binary `-` operator is overloaded, the `-=` operator is also implicitly overloaded. A user-defined type can't explicitly overload the `-=` operator. +A user-defined type can [overload](operator-overloading.md) the `-` operator. When a binary `-` operator is overloaded, the `-=` operator is also implicitly overloaded. Beginning with C# 14, a user-defined type can explicitly overload the `-=` operator to provide a more efficient implementation. Typically, a type overloads the `-=` operator because the value can be updated in place, rather than allocating performing the addition and return a new instance. If a type doesn't provide an explicit overload, the compiler generates the implicit overload. ## C# language specification -For more information, see the [Unary minus operator](~/_csharpstandard/standard/expressions.md#1293-unary-minus-operator) and [Subtraction operator](~/_csharpstandard/standard/expressions.md#12106-subtraction-operator) sections of the [C# language specification](~/_csharpstandard/standard/README.md). +For more information, see the [Unary minus operator](~/_csharpstandard/standard/expressions.md#1293-unary-minus-operator) and [Subtraction operator](~/_csharpstandard/standard/expressions.md#12106-subtraction-operator) sections of the [C# language specification](~/_csharpstandard/standard/README.md). For more information on overloading the compound assignment operators in C# 14 and later, see the [user defined compound assignment](~/_csharplang/proposals/user-defined-compound-assignment.md) feature specification. ## See also From 2441ab61dd8cf7debdeeae32e93885a002cd07dd Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 10 Jun 2025 17:53:28 -0400 Subject: [PATCH 03/11] lint --- docs/csharp/language-reference/operators/arithmetic-operators.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/csharp/language-reference/operators/arithmetic-operators.md b/docs/csharp/language-reference/operators/arithmetic-operators.md index d792d475e3d17..72dfa149f3f8b 100644 --- a/docs/csharp/language-reference/operators/arithmetic-operators.md +++ b/docs/csharp/language-reference/operators/arithmetic-operators.md @@ -292,7 +292,6 @@ For more information, see the following sections of the [C# language specificati - [Numeric promotions](~/_csharpstandard/standard/expressions.md#1247-numeric-promotions) - [User defined compound assignment](~/_csharplang/proposals/user-defined-compound-assignment.md) - ## See also - [C# operators and expressions](index.md) From 595eaa764fdff040863428c8c551ccc6a36bbf16 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 11 Jun 2025 12:04:54 -0400 Subject: [PATCH 04/11] update operator overloading reference. --- .../operators/operator-overloading.md | 49 ++++++++++-- .../snippets/shared/OperatorOverloading.cs | 80 +++++++++++++++---- 2 files changed, 105 insertions(+), 24 deletions(-) diff --git a/docs/csharp/language-reference/operators/operator-overloading.md b/docs/csharp/language-reference/operators/operator-overloading.md index 98a9375f0cffa..14b48de0c2aad 100644 --- a/docs/csharp/language-reference/operators/operator-overloading.md +++ b/docs/csharp/language-reference/operators/operator-overloading.md @@ -1,7 +1,7 @@ --- title: "Operator overloading - Define unary, arithmetic, equality, and comparison operators." -description: "Learn how to overload a C# operator and which C# operators are overloadable. In general, the unary, arithmetic, equality and comparison operators are overloadable." -ms.date: 11/28/2022 +description: "Learn how to overload a C# operator and which C# operators are overloadable. In general, the unary, arithmetic, equality, and comparison operators are overloadable." +ms.date: 06/11/2025 f1_keywords: - "operator_CSharpKeyword" - operator @@ -9,18 +9,20 @@ helpviewer_keywords: - "operator keyword [C#]" - "operator overloading [C#]" --- -# Operator overloading - predefined unary, arithmetic, equality and comparison operators +# Operator overloading - predefined unary, arithmetic, equality, and comparison operators A user-defined type can overload a predefined C# operator. That is, a type can provide the custom implementation of an operation in case one or both of the operands are of that type. The [Overloadable operators](#overloadable-operators) section shows which C# operators can be overloaded. Use the `operator` keyword to declare an operator. An operator declaration must satisfy the following rules: -- It includes both a `public` and a `static` modifier. +- It includes both a `public` modifier. - A unary operator has one input parameter. A binary operator has two input parameters. In each case, at least one parameter must have type `T` or `T?` where `T` is the type that contains the operator declaration. +- It includes the `static` modifier, except for the compound assignment operators, such as `+=`. +- The increment (`++`) and decrement (`--`) operators can be implemented as either static or instance methods. The following example defines a simplified structure to represent a rational number. The structure overloads some of the [arithmetic operators](arithmetic-operators.md): -[!code-csharp[fraction example](snippets/shared/OperatorOverloading.cs)] +:::code language="csharp" source="snippets/shared/OperatorOverloading.cs"::: You could extend the preceding example by [defining an implicit conversion](user-defined-conversion-operators.md) from `int` to `Fraction`. Then, overloaded operators would support arguments of those two types. That is, it would become possible to add an integer to a fraction and obtain a fraction as a result. @@ -35,6 +37,21 @@ The following table shows the operators that can be overloaded: |[`+x`](arithmetic-operators.md#unary-plus-and-minus-operators), [`-x`](arithmetic-operators.md#unary-plus-and-minus-operators), [`!x`](boolean-logical-operators.md#logical-negation-operator-), [`~x`](bitwise-and-shift-operators.md#bitwise-complement-operator-), [`++`](arithmetic-operators.md#increment-operator-), [`--`](arithmetic-operators.md#decrement-operator---), [`true`](true-false-operators.md), [`false`](true-false-operators.md)|The `true` and `false` operators must be overloaded together.| |[`x + y`](arithmetic-operators.md#addition-operator-), [`x - y`](arithmetic-operators.md#subtraction-operator--), [`x * y`](arithmetic-operators.md#multiplication-operator-), [`x / y`](arithmetic-operators.md#division-operator-), [`x % y`](arithmetic-operators.md#remainder-operator-),
[`x & y`](boolean-logical-operators.md#logical-and-operator-), [x | y](boolean-logical-operators.md#logical-or-operator-), [`x ^ y`](boolean-logical-operators.md#logical-exclusive-or-operator-),
[`x << y`](bitwise-and-shift-operators.md#left-shift-operator-), [`x >> y`](bitwise-and-shift-operators.md#right-shift-operator-), [`x >>> y`](bitwise-and-shift-operators.md#unsigned-right-shift-operator-) | | |[`x == y`](equality-operators.md#equality-operator-), [`x != y`](equality-operators.md#inequality-operator-), [`x < y`](comparison-operators.md#less-than-operator-), [`x > y`](comparison-operators.md#greater-than-operator-), [`x <= y`](comparison-operators.md#less-than-or-equal-operator-), [`x >= y`](comparison-operators.md#greater-than-or-equal-operator-)|Must be overloaded in pairs as follows: `==` and `!=`, `<` and `>`, `<=` and `>=`.| +|[`+=`](arithmetic-operators.md#compound-assignment), [`-=`](arithmetic-operators.md#compound-assignment), [`*=`](arithmetic-operators.md#compound-assignment), [`/=`](arithmetic-operators.md#compound-assignment), [`%=`](arithmetic-operators.md#compound-assignment), [`&=`](boolean-logical-operators.md#compound-assignment), [`\|=``](boolean-logical-operators.md#compound-assignment), [`^=`](boolean-logical-operators.md#compound-assignment), [`<<=`](bitwise-and-shift-operators.md#compound-assignment), [`>>=`](bitwise-and-shift-operators.md#compound-assignment), [`>>>=`](bitwise-and-shift-operators.md#compound-assignment)|The compound assignment operators can be overloaded in C# 14 and later.| + +The compound assignment overloaded operators must follow these rules: + +- It must include the `public` modifier. +- It can't include the `static` modifier. +- The return type must be `void`. +- The declaration must include one parameter, which represents the right hand side of the compound assignment. + +Beginning with C# 14, the increment (`++`) and decrement (`--`) operators can be overloaded as instance members. Instance operators can improve performance by avoiding the creation of a new instance. The instance operators must follow these rules: + +- It must include the `public` modifier. +- It can't include the `static` modifier. +- The return type must be `void`. +- It can't declare any parameters, even if those parameters have a default value. ## Non overloadable operators @@ -44,10 +61,28 @@ The following table shows the operators that can't be overloaded: | :---------: | --------------- | |[`x && y`](boolean-logical-operators.md#conditional-logical-and-operator-), [x || y](boolean-logical-operators.md#conditional-logical-or-operator-)| Overload both the [`true`](true-false-operators.md) and [`false`](true-false-operators.md) operators and the [`&`](boolean-logical-operators.md#logical-and-operator-) or [|](boolean-logical-operators.md#logical-or-operator-) operators. For more information, see [User-defined conditional logical operators](~/_csharpstandard/standard/expressions.md#12143-user-defined-conditional-logical-operators).| |[a[i]](member-access-operators.md#indexer-operator-), [`a?[i]`](member-access-operators.md#null-conditional-operators--and-)|Define an [indexer](../../programming-guide/indexers/index.md).| -|[`(T)x`](type-testing-and-cast.md#cast-expression)|Define custom type conversions that can be performed by a cast expression. For more information, see [User-defined conversion operators](user-defined-conversion-operators.md).| -|[`+=`](arithmetic-operators.md#compound-assignment), [`-=`](arithmetic-operators.md#compound-assignment), [`*=`](arithmetic-operators.md#compound-assignment), [`/=`](arithmetic-operators.md#compound-assignment), [`%=`](arithmetic-operators.md#compound-assignment), [`&=`](boolean-logical-operators.md#compound-assignment), [|=](boolean-logical-operators.md#compound-assignment), [`^=`](boolean-logical-operators.md#compound-assignment), [`<<=`](bitwise-and-shift-operators.md#compound-assignment), [`>>=`](bitwise-and-shift-operators.md#compound-assignment), [`>>>=`](bitwise-and-shift-operators.md#compound-assignment)|Overload the corresponding binary operator. For example, when you overload the binary `+` operator, `+=` is implicitly overloaded.| +|[`(T)x`](type-testing-and-cast.md#cast-expression)|Define custom type conversions performed by a cast expression. For more information, see [User-defined conversion operators](user-defined-conversion-operators.md).| |[`^x`](member-access-operators.md#index-from-end-operator-), [`x = y`](assignment-operator.md), [`x.y`](member-access-operators.md#member-access-expression-), [`x?.y`](member-access-operators.md#null-conditional-operators--and-), [`c ? t : f`](conditional-operator.md), [`x ?? y`](null-coalescing-operator.md), [`??= y`](null-coalescing-operator.md),
[`x..y`](member-access-operators.md#range-operator-), [`x->y`](pointer-related-operators.md#pointer-member-access-operator--), [`=>`](lambda-operator.md), [`f(x)`](member-access-operators.md#invocation-expression-), [`as`](type-testing-and-cast.md#the-as-operator), [`await`](await.md), [`checked`](../statements/checked-and-unchecked.md), [`unchecked`](../statements/checked-and-unchecked.md), [`default`](default.md), [`delegate`](delegate-operator.md), [`is`](type-testing-and-cast.md#the-is-operator), [`nameof`](nameof.md), [`new`](new-operator.md),
[`sizeof`](sizeof.md), [`stackalloc`](stackalloc.md), [`switch`](switch-expression.md), [`typeof`](type-testing-and-cast.md#the-typeof-operator), [`with`](with-expression.md)|None.| +Before C# 14, the compound operators can't be overloaded. Overloading the corresponding binary operator implicitly overloads the corresponding compound assignment operator. + +## Operator overload resolution + +> [!IMPORTANT] This section applies to C# 14 and later. Before C# 14, compound assignment operators and instance increment and decrement operators aren't allowed. + +If `x` is classified as a variable in a compound assignment expression such as `x «op»= y`, instance operators are preferred over any static operator for `«op»`. If an overloaded `«op»=` operator isn't declared for the type of `x` or `x` isn't classified as a variable, the static operators are used. + +For the postfix operator `++`, if `x` isn't classified as a variable *or* the expression `x++` is used, the instance `operator++` is ignored. Otherwise, preference is given to the instance `operator ++`. For example, + +```csharp +x++; // Instance operator++ preferred. +y = x++; // instance operator++ isn't considered. +``` + +The reason for this rule is that `y` should be assigned to the value of `x` *before* it's incremented. The compiler can't determine that for a user-defined implementation in a reference type. + +For the prefix operator `++`, if `x` is classified as a variable in `++x`, the instance operator is preferred over a static unary operator. + ## C# language specification For more information, see the following sections of the [C# language specification](~/_csharpstandard/standard/README.md): diff --git a/docs/csharp/language-reference/operators/snippets/shared/OperatorOverloading.cs b/docs/csharp/language-reference/operators/snippets/shared/OperatorOverloading.cs index 791b7770f0230..643b2cbab8f03 100644 --- a/docs/csharp/language-reference/operators/snippets/shared/OperatorOverloading.cs +++ b/docs/csharp/language-reference/operators/snippets/shared/OperatorOverloading.cs @@ -1,7 +1,7 @@ -public readonly struct Fraction +public struct Fraction { - private readonly int num; - private readonly int den; + private int numerator; + private int denominator; public Fraction(int numerator, int denominator) { @@ -9,32 +9,78 @@ public Fraction(int numerator, int denominator) { throw new ArgumentException("Denominator cannot be zero.", nameof(denominator)); } - num = numerator; - den = denominator; + this.numerator = numerator; + this.denominator = denominator; } - public static Fraction operator +(Fraction a) => a; - public static Fraction operator -(Fraction a) => new Fraction(-a.num, a.den); + public static Fraction operator +(Fraction operand) => operand; + public static Fraction operator -(Fraction operand) => new Fraction(-operand.numerator, operand.denominator); - public static Fraction operator +(Fraction a, Fraction b) - => new Fraction(a.num * b.den + b.num * a.den, a.den * b.den); + public static Fraction operator +(Fraction left, Fraction right) + => new Fraction(left.numerator * right.denominator + right.numerator * left.denominator, left.denominator * right.denominator); - public static Fraction operator -(Fraction a, Fraction b) - => a + (-b); + public static Fraction operator -(Fraction left, Fraction right) + => left + (-right); - public static Fraction operator *(Fraction a, Fraction b) - => new Fraction(a.num * b.num, a.den * b.den); + public static Fraction operator *(Fraction left, Fraction right) + => new Fraction(left.numerator * right.numerator, left.denominator * right.denominator); - public static Fraction operator /(Fraction a, Fraction b) + public static Fraction operator /(Fraction left, Fraction right) { - if (b.num == 0) + if (right.numerator == 0) { throw new DivideByZeroException(); } - return new Fraction(a.num * b.den, a.den * b.num); + return new Fraction(left.numerator * right.denominator, left.denominator * right.numerator); } - public override string ToString() => $"{num} / {den}"; + // Define increment and decrement to add 1/den, rather than 1/1. + public static Fraction operator ++(Fraction operand) + => new Fraction(operand.numerator++, operand.denominator); + + public static Fraction operator --(Fraction operand) => + new Fraction(operand.numerator--, operand.denominator); + + public override string ToString() => $"{numerator} / {denominator}"; + + // New operators allowed in C# 14: + public void operator +=(Fraction operand) => + (numerator, denominator ) = + ( + numerator * operand.denominator + operand.numerator * denominator, + denominator * operand.denominator + ); + + public void operator -=(Fraction operand) => + (numerator, denominator) = + ( + numerator * operand.denominator - operand.numerator * denominator, + denominator * operand.denominator + ); + + public void operator *=(Fraction operand) => + (numerator, denominator) = + ( + numerator * operand.numerator, + denominator * operand.denominator + ); + + public void operator /=(Fraction operand) + { + if (operand.numerator == 0) + { + throw new DivideByZeroException(); + } + (numerator, denominator) = + ( + numerator * operand.denominator, + denominator * operand.numerator + ); + } + + public void operator ++() => numerator++; + + public void operator --() => numerator--; } public static class OperatorOverloading From 11235a69ae79c61d83bb3672eaa77a824246d8ff Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 11 Jun 2025 13:08:54 -0400 Subject: [PATCH 05/11] formatting issue --- .../csharp/language-reference/operators/operator-overloading.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/csharp/language-reference/operators/operator-overloading.md b/docs/csharp/language-reference/operators/operator-overloading.md index 14b48de0c2aad..8e6bc1665538b 100644 --- a/docs/csharp/language-reference/operators/operator-overloading.md +++ b/docs/csharp/language-reference/operators/operator-overloading.md @@ -37,7 +37,7 @@ The following table shows the operators that can be overloaded: |[`+x`](arithmetic-operators.md#unary-plus-and-minus-operators), [`-x`](arithmetic-operators.md#unary-plus-and-minus-operators), [`!x`](boolean-logical-operators.md#logical-negation-operator-), [`~x`](bitwise-and-shift-operators.md#bitwise-complement-operator-), [`++`](arithmetic-operators.md#increment-operator-), [`--`](arithmetic-operators.md#decrement-operator---), [`true`](true-false-operators.md), [`false`](true-false-operators.md)|The `true` and `false` operators must be overloaded together.| |[`x + y`](arithmetic-operators.md#addition-operator-), [`x - y`](arithmetic-operators.md#subtraction-operator--), [`x * y`](arithmetic-operators.md#multiplication-operator-), [`x / y`](arithmetic-operators.md#division-operator-), [`x % y`](arithmetic-operators.md#remainder-operator-),
[`x & y`](boolean-logical-operators.md#logical-and-operator-), [x | y](boolean-logical-operators.md#logical-or-operator-), [`x ^ y`](boolean-logical-operators.md#logical-exclusive-or-operator-),
[`x << y`](bitwise-and-shift-operators.md#left-shift-operator-), [`x >> y`](bitwise-and-shift-operators.md#right-shift-operator-), [`x >>> y`](bitwise-and-shift-operators.md#unsigned-right-shift-operator-) | | |[`x == y`](equality-operators.md#equality-operator-), [`x != y`](equality-operators.md#inequality-operator-), [`x < y`](comparison-operators.md#less-than-operator-), [`x > y`](comparison-operators.md#greater-than-operator-), [`x <= y`](comparison-operators.md#less-than-or-equal-operator-), [`x >= y`](comparison-operators.md#greater-than-or-equal-operator-)|Must be overloaded in pairs as follows: `==` and `!=`, `<` and `>`, `<=` and `>=`.| -|[`+=`](arithmetic-operators.md#compound-assignment), [`-=`](arithmetic-operators.md#compound-assignment), [`*=`](arithmetic-operators.md#compound-assignment), [`/=`](arithmetic-operators.md#compound-assignment), [`%=`](arithmetic-operators.md#compound-assignment), [`&=`](boolean-logical-operators.md#compound-assignment), [`\|=``](boolean-logical-operators.md#compound-assignment), [`^=`](boolean-logical-operators.md#compound-assignment), [`<<=`](bitwise-and-shift-operators.md#compound-assignment), [`>>=`](bitwise-and-shift-operators.md#compound-assignment), [`>>>=`](bitwise-and-shift-operators.md#compound-assignment)|The compound assignment operators can be overloaded in C# 14 and later.| +|[`+=`](arithmetic-operators.md#compound-assignment), [`-=`](arithmetic-operators.md#compound-assignment), [`*=`](arithmetic-operators.md#compound-assignment), [`/=`](arithmetic-operators.md#compound-assignment), [`%=`](arithmetic-operators.md#compound-assignment), [`&=`](boolean-logical-operators.md#compound-assignment), [`\|=`](boolean-logical-operators.md#compound-assignment), [`^=`](boolean-logical-operators.md#compound-assignment), [`<<=`](bitwise-and-shift-operators.md#compound-assignment), [`>>=`](bitwise-and-shift-operators.md#compound-assignment), [`>>>=`](bitwise-and-shift-operators.md#compound-assignment)|The compound assignment operators can be overloaded in C# 14 and later.| The compound assignment overloaded operators must follow these rules: From e3cb5a79e6a768bf00b8aad5b4e458f843aee943 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 11 Jun 2025 13:14:40 -0400 Subject: [PATCH 06/11] build issue --- .../language-reference/operators/operator-overloading.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/csharp/language-reference/operators/operator-overloading.md b/docs/csharp/language-reference/operators/operator-overloading.md index 8e6bc1665538b..9bdc7fd9d1996 100644 --- a/docs/csharp/language-reference/operators/operator-overloading.md +++ b/docs/csharp/language-reference/operators/operator-overloading.md @@ -68,7 +68,8 @@ Before C# 14, the compound operators can't be overloaded. Overloading the corres ## Operator overload resolution -> [!IMPORTANT] This section applies to C# 14 and later. Before C# 14, compound assignment operators and instance increment and decrement operators aren't allowed. +> [!IMPORTANT] +> This section applies to C# 14 and later. Before C# 14, compound assignment operators and instance increment and decrement operators aren't allowed. If `x` is classified as a variable in a compound assignment expression such as `x «op»= y`, instance operators are preferred over any static operator for `«op»`. If an overloaded `«op»=` operator isn't declared for the type of `x` or `x` isn't classified as a variable, the static operators are used. From 0f1c66f477fa2a18d32386c2cc92db913a0e67eb Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 11 Jun 2025 15:03:20 -0400 Subject: [PATCH 07/11] remove outdated restrictions. --- .../programming-guide/classes-and-structs/access-modifiers.md | 2 +- docs/csharp/programming-guide/classes-and-structs/members.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/csharp/programming-guide/classes-and-structs/access-modifiers.md b/docs/csharp/programming-guide/classes-and-structs/access-modifiers.md index b3a3f8577cc5e..ed954559d7883 100644 --- a/docs/csharp/programming-guide/classes-and-structs/access-modifiers.md +++ b/docs/csharp/programming-guide/classes-and-structs/access-modifiers.md @@ -65,7 +65,7 @@ Normally, the accessibility of a member isn't greater than the accessibility of The type of any member field, property, or event must be at least as accessible as the member itself. Similarly, the return type and the parameter types of any method, indexer, or delegate must be at least as accessible as the member itself. For example, you can't have a `public` method `M` that returns a class `C` unless `C` is also `public`. Likewise, you can't have a `protected` property of type `A` if `A` is declared as `private`. -User-defined operators must always be declared as `public` and `static`. For more information, see [Operator overloading](../../language-reference/operators/operator-overloading.md). +User-defined operators must always be declared as `public`. For more information, see [Operator overloading](../../language-reference/operators/operator-overloading.md). To set the access level for a `class` or `struct` member, add the appropriate keyword to the member declaration, as shown in the following example. diff --git a/docs/csharp/programming-guide/classes-and-structs/members.md b/docs/csharp/programming-guide/classes-and-structs/members.md index 22e3f09fee178..cd537a3b63aea 100644 --- a/docs/csharp/programming-guide/classes-and-structs/members.md +++ b/docs/csharp/programming-guide/classes-and-structs/members.md @@ -20,7 +20,7 @@ Classes and structs have members that represent their data and behavior. A class |[Properties](./properties.md)|Properties are methods on a class that are accessed as if they were fields on that class. A property can provide protection for a class field to keep it from being changed without the knowledge of the object.| |[Methods](./methods.md)|Methods define the actions that a class can perform. Methods can take parameters that provide input data, and can return output data through parameters. Methods can also return a value directly, without using a parameter.| |[Events](../events/index.md)|Events provide notifications about occurrences, such as button clicks or the successful completion of a method, to other objects. Events are defined and triggered by using delegates.| -|[Operators](../../language-reference/operators/index.md)|Overloaded operators are considered type members. When you overload an operator, you define it as a public static method in a type. For more information, see [Operator overloading](../../language-reference/operators/operator-overloading.md).| +|[Operators](../../language-reference/operators/index.md)|Overloaded operators are considered type members. When you overload an operator, you define it as a public method in a type. For more information, see [Operator overloading](../../language-reference/operators/operator-overloading.md).| |[Indexers](../indexers/index.md)|Indexers enable an object to be indexed in a manner similar to arrays.| |[Constructors](./constructors.md)|Constructors are methods that are called when the object is first created. They are often used to initialize the data of an object.| |[Finalizers](./finalizers.md)|Finalizers are used very rarely in C#. They are methods that are called by the runtime execution engine when the object is about to be removed from memory. They are generally used to make sure that any resources which must be released are handled appropriately.| From 2c2bb770b57bdd1ca92c2fdd19beb98304172ebc Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 12 Jun 2025 11:04:13 -0400 Subject: [PATCH 08/11] Update docs/csharp/language-reference/operators/operator-overloading.md --- .../csharp/language-reference/operators/operator-overloading.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/csharp/language-reference/operators/operator-overloading.md b/docs/csharp/language-reference/operators/operator-overloading.md index 9bdc7fd9d1996..29b576c86e1e0 100644 --- a/docs/csharp/language-reference/operators/operator-overloading.md +++ b/docs/csharp/language-reference/operators/operator-overloading.md @@ -69,7 +69,7 @@ Before C# 14, the compound operators can't be overloaded. Overloading the corres ## Operator overload resolution > [!IMPORTANT] -> This section applies to C# 14 and later. Before C# 14, compound assignment operators and instance increment and decrement operators aren't allowed. +> This section applies to C# 14 and later. Before C# 14, user-defined compound assignment operators and instance increment and decrement operators aren't allowed. If `x` is classified as a variable in a compound assignment expression such as `x «op»= y`, instance operators are preferred over any static operator for `«op»`. If an overloaded `«op»=` operator isn't declared for the type of `x` or `x` isn't classified as a variable, the static operators are used. From 2d6649c49f526984bbc8bd94bbfd1ae44bc8ab61 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 12 Jun 2025 11:12:37 -0400 Subject: [PATCH 09/11] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/core/whats-new/dotnet-10/overview.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/core/whats-new/dotnet-10/overview.md b/docs/core/whats-new/dotnet-10/overview.md index ebe9a5c740a1c..f388c69ea85cb 100644 --- a/docs/core/whats-new/dotnet-10/overview.md +++ b/docs/core/whats-new/dotnet-10/overview.md @@ -54,8 +54,8 @@ C# 14 introduces several new features and enhancements to improve developer prod - Support for partial instance constructors and partial events, complementing partial methods and properties introduced in C# 13. - New `extension` blocks add support for static extension methods, and static and instance extension properties. - Null-conditional assignment using the `?.` operator. -- User defined compound assignment operators like `+=` and `-=`. -- User defined increment (`++`) and decrement (`--`) operators. +- User-defined compound assignment operators like `+=` and `-=`. +- User-defined increment (`++`) and decrement (`--`) operators. For more information, see [What's new in C# 14](../../../csharp/whats-new/csharp-14.md). From ddf6b72716434fee290149277f1d1167b1a4cf0d Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 12 Jun 2025 12:57:25 -0400 Subject: [PATCH 10/11] Apply suggestions from code review Co-authored-by: Genevieve Warren <24882762+gewarren@users.noreply.github.com> --- docfx.json | 2 +- .../language-reference/operators/arithmetic-operators.md | 2 +- .../language-reference/operators/operator-overloading.md | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docfx.json b/docfx.json index 76bfa5afcd650..56db322f914e2 100644 --- a/docfx.json +++ b/docfx.json @@ -819,7 +819,7 @@ "_csharplang/proposals/partial-events-and-constructors.md": "This proposal allows partial events and constructors to be declared in partial classes. The event and constructor can be split across class declarations.", "_csharplang/proposals/null-conditional-assignment.md": "This proposal allows the null conditional operator to be used for the destination of assignment expressions. This allows you to assign a value to a property or field only if the left side is not null.", "_csharplang/proposals/extensions.md": "This proposal enables new kinds of extension members. These new extension members support extension properties, extension static members, including extension operators.", - "_csharplang/proposals/user-defined-compound-assignment.md": "This proposal introduces user-defined compound assignment operators. Developers can override compound assignment, increment and decrement operators.", + "_csharplang/proposals/user-defined-compound-assignment.md": "This proposal introduces user-defined compound assignment operators. Developers can override compound assignment, increment, and decrement operators.", "_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", diff --git a/docs/csharp/language-reference/operators/arithmetic-operators.md b/docs/csharp/language-reference/operators/arithmetic-operators.md index 72dfa149f3f8b..38cf96f79c0c9 100644 --- a/docs/csharp/language-reference/operators/arithmetic-operators.md +++ b/docs/csharp/language-reference/operators/arithmetic-operators.md @@ -40,7 +40,7 @@ The following operators perform arithmetic operations with operands of numeric t All [integral](../builtin-types/integral-numeric-types.md) and [floating-point](../builtin-types/floating-point-numeric-types.md) numeric types support these operators. -The `int`, `uint`, `long`, and `ulong` types define all these operators. The other integral types (`sbyte`, `byte`, `short`, `ushort`, or `char`) define only the `++` and `--` operators. For other operators, When operands are of the integral types `sbyte`, `byte`, `short`, `ushort`, or `char`, their values are converted to the `int` type and the result type is `int`. When operands are of different integral or floating-point types, their values are converted to the closest containing type, if such a type exists. For more information, see the [Numeric promotions](~/_csharpstandard/standard/expressions.md#1247-numeric-promotions) section of the [C# language specification](~/_csharpstandard/standard/README.md). The `++` and `--` operators are defined for all integral and floating-point numeric types and the [char](../builtin-types/char.md) type. The result type of a [compound assignment expression](#compound-assignment) is the type of the left-hand operand. +The `int`, `uint`, `long`, and `ulong` types define all these operators. The other integral types (`sbyte`, `byte`, `short`, `ushort`, and `char`) define only the `++` and `--` operators. For other operators, when operands are of the integral types `sbyte`, `byte`, `short`, `ushort`, or `char`, their values are converted to the `int` type and the result type is `int`. When operands are of different integral or floating-point types, their values are converted to the closest containing type, if such a type exists. For more information, see the [Numeric promotions](~/_csharpstandard/standard/expressions.md#1247-numeric-promotions) section of the [C# language specification](~/_csharpstandard/standard/README.md). The `++` and `--` operators are defined for all integral and floating-point numeric types and the [char](../builtin-types/char.md) type. The result type of a [compound assignment expression](#compound-assignment) is the type of the left-hand operand. ## Increment operator ++ diff --git a/docs/csharp/language-reference/operators/operator-overloading.md b/docs/csharp/language-reference/operators/operator-overloading.md index 29b576c86e1e0..2e411016eb6af 100644 --- a/docs/csharp/language-reference/operators/operator-overloading.md +++ b/docs/csharp/language-reference/operators/operator-overloading.md @@ -15,7 +15,7 @@ A user-defined type can overload a predefined C# operator. That is, a type can p Use the `operator` keyword to declare an operator. An operator declaration must satisfy the following rules: -- It includes both a `public` modifier. +- It includes a `public` modifier. - A unary operator has one input parameter. A binary operator has two input parameters. In each case, at least one parameter must have type `T` or `T?` where `T` is the type that contains the operator declaration. - It includes the `static` modifier, except for the compound assignment operators, such as `+=`. - The increment (`++`) and decrement (`--`) operators can be implemented as either static or instance methods. @@ -39,14 +39,14 @@ The following table shows the operators that can be overloaded: |[`x == y`](equality-operators.md#equality-operator-), [`x != y`](equality-operators.md#inequality-operator-), [`x < y`](comparison-operators.md#less-than-operator-), [`x > y`](comparison-operators.md#greater-than-operator-), [`x <= y`](comparison-operators.md#less-than-or-equal-operator-), [`x >= y`](comparison-operators.md#greater-than-or-equal-operator-)|Must be overloaded in pairs as follows: `==` and `!=`, `<` and `>`, `<=` and `>=`.| |[`+=`](arithmetic-operators.md#compound-assignment), [`-=`](arithmetic-operators.md#compound-assignment), [`*=`](arithmetic-operators.md#compound-assignment), [`/=`](arithmetic-operators.md#compound-assignment), [`%=`](arithmetic-operators.md#compound-assignment), [`&=`](boolean-logical-operators.md#compound-assignment), [`\|=`](boolean-logical-operators.md#compound-assignment), [`^=`](boolean-logical-operators.md#compound-assignment), [`<<=`](bitwise-and-shift-operators.md#compound-assignment), [`>>=`](bitwise-and-shift-operators.md#compound-assignment), [`>>>=`](bitwise-and-shift-operators.md#compound-assignment)|The compound assignment operators can be overloaded in C# 14 and later.| -The compound assignment overloaded operators must follow these rules: +A compound assignment overloaded operator must follow these rules: - It must include the `public` modifier. - It can't include the `static` modifier. - The return type must be `void`. - The declaration must include one parameter, which represents the right hand side of the compound assignment. -Beginning with C# 14, the increment (`++`) and decrement (`--`) operators can be overloaded as instance members. Instance operators can improve performance by avoiding the creation of a new instance. The instance operators must follow these rules: +Beginning with C# 14, the increment (`++`) and decrement (`--`) operators can be overloaded as instance members. Instance operators can improve performance by avoiding the creation of a new instance. An instance operator must follow these rules: - It must include the `public` modifier. - It can't include the `static` modifier. From b35174c5702b00fa9ce4a8e4e6b0da384ebb2d7e Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 12 Jun 2025 16:05:17 -0400 Subject: [PATCH 11/11] Respond to feedback. --- docs/csharp/language-reference/operators/addition-operator.md | 2 +- .../csharp/language-reference/operators/arithmetic-operators.md | 2 +- docs/csharp/language-reference/operators/assignment-operator.md | 2 +- .../language-reference/operators/bitwise-and-shift-operators.md | 2 +- .../language-reference/operators/boolean-logical-operators.md | 2 +- .../csharp/language-reference/operators/subtraction-operator.md | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/csharp/language-reference/operators/addition-operator.md b/docs/csharp/language-reference/operators/addition-operator.md index d5fe4323d9568..14aea62365bda 100644 --- a/docs/csharp/language-reference/operators/addition-operator.md +++ b/docs/csharp/language-reference/operators/addition-operator.md @@ -68,7 +68,7 @@ You also use the `+=` operator to specify an event handler method when you subsc ## Operator overloadability -A user-defined type can [overload](operator-overloading.md) the `+` operator. When a binary `+` operator is overloaded, the `+=` operator is also implicitly overloaded. Beginning with C# 14, a user-defined type can explicitly overload the `+=` operator to provide a more efficient implementation. Typically, a type overloads the `+=` operator because the value can be updated in place, rather than allocating performing the addition and return a new instance. If a type doesn't provide an explicit overload, the compiler generates the implicit overload. +A user-defined type can [overload](operator-overloading.md) the `+` operator. When a binary `+` operator is overloaded, the `+=` operator is also implicitly overloaded. Beginning with C# 14, a user-defined type can explicitly overload the `+=` operator to provide a more efficient implementation. Typically, a type overloads the `+=` operator because the value can be updated in place, rather than allocate a new instance to hold the result of the addition. If a type doesn't provide an explicit overload, the compiler generates the implicit overload. ## C# language specification diff --git a/docs/csharp/language-reference/operators/arithmetic-operators.md b/docs/csharp/language-reference/operators/arithmetic-operators.md index 38cf96f79c0c9..f90246855393b 100644 --- a/docs/csharp/language-reference/operators/arithmetic-operators.md +++ b/docs/csharp/language-reference/operators/arithmetic-operators.md @@ -247,7 +247,7 @@ For more information, see remarks at the [System.Double](/dotnet/api/system.doub ## Operator overloadability -A user-defined type can [overload](operator-overloading.md) the unary (`++`, `--`, `+`, and `-`) and binary (`*`, `/`, `%`, `+`, and `-`) arithmetic operators. When a binary operator is overloaded, the corresponding compound assignment operator is also implicitly overloaded. Beginning with C# 14, a user-defined type can explicitly overload the compound assignment operators (`op=`) to provide a more efficient implementation. Typically, a type overloads these operators because the value can be updated in place, rather than allocating performing the addition and return a new instance. If a type doesn't provide an explicit overload, the compiler generates the implicit overload. +A user-defined type can [overload](operator-overloading.md) the unary (`++`, `--`, `+`, and `-`) and binary (`*`, `/`, `%`, `+`, and `-`) arithmetic operators. When a binary operator is overloaded, the corresponding compound assignment operator is also implicitly overloaded. Beginning with C# 14, a user-defined type can explicitly overload the compound assignment operators (`op=`) to provide a more efficient implementation. Typically, a type overloads these operators because the value can be updated in place, rather than allocating a new instance to store the result of the operation. If a type doesn't provide an explicit overload, the compiler generates the implicit overload. ### User-defined checked operators diff --git a/docs/csharp/language-reference/operators/assignment-operator.md b/docs/csharp/language-reference/operators/assignment-operator.md index 4a178f5a04d55..db8f86ff5bd12 100644 --- a/docs/csharp/language-reference/operators/assignment-operator.md +++ b/docs/csharp/language-reference/operators/assignment-operator.md @@ -77,7 +77,7 @@ You can use the null-coalescing assignment operator `??=` to assign the value of A user-defined type can't [overload](operator-overloading.md) the assignment operator. However, a user-defined type can define an implicit conversion to another type. That way, the value of a user-defined type can be assigned to a variable, a property, or an indexer element of another type. For more information, see [User-defined conversion operators](user-defined-conversion-operators.md). -If a user-defined type overloads a binary operator `op`, the `op=` operator, if it exists, is also implicitly overloaded. Beginning with C# 14, a user-defined type can explicitly overload the compound assignment operators (`op=`) to provide a more efficient implementation. Typically, a type overloads these operators because the value can be updated in place, rather than allocating performing the addition and return a new instance. If a type doesn't provide an explicit overload, the compiler generates the implicit overload. +If a user-defined type overloads a binary operator `op`, the `op=` operator, if it exists, is also implicitly overloaded. Beginning with C# 14, a user-defined type can explicitly overload the compound assignment operators (`op=`) to provide a more efficient implementation. Typically, a type overloads these operators because the value can be updated in place, rather than allocating a new instance to hold the result of the binary operation. If a type doesn't provide an explicit overload, the compiler generates the implicit overload. ## C# language specification diff --git a/docs/csharp/language-reference/operators/bitwise-and-shift-operators.md b/docs/csharp/language-reference/operators/bitwise-and-shift-operators.md index 96c6c1658817a..3a8151e71719b 100644 --- a/docs/csharp/language-reference/operators/bitwise-and-shift-operators.md +++ b/docs/csharp/language-reference/operators/bitwise-and-shift-operators.md @@ -186,7 +186,7 @@ You typically use bitwise logical operators with an enumeration type that is def ## Operator overloadability -A user-defined type can [overload](operator-overloading.md) the `~`, `<<`, `>>`, `>>>`, `&`, `|`, and `^` operators. When a binary operator is overloaded, the corresponding compound assignment operator is also implicitly overloaded. Beginning with C# 14, a user-defined type can explicitly overload the compound assignment operators to provide a more efficient implementation. Typically, a type overloads these operators because the value can be updated in place, rather than allocating performing the addition and return a new instance. If a type doesn't provide an explicit overload, the compiler generates the implicit overload. +A user-defined type can [overload](operator-overloading.md) the `~`, `<<`, `>>`, `>>>`, `&`, `|`, and `^` operators. When a binary operator is overloaded, the corresponding compound assignment operator is also implicitly overloaded. Beginning with C# 14, a user-defined type can explicitly overload the compound assignment operators to provide a more efficient implementation. Typically, a type overloads these operators because the value can be updated in place, rather than allocating a new instance to store the result of the binary operation. If a type doesn't provide an explicit overload, the compiler generates the implicit overload. If a user-defined type `T` overloads the `<<`, `>>`, or `>>>` operator, the type of the left-hand operand must be `T`. In C# 10 and earlier, the type of the right-hand operand must be `int`; beginning with C# 11, the type of the right-hand operand of an overloaded shift operator can be any. diff --git a/docs/csharp/language-reference/operators/boolean-logical-operators.md b/docs/csharp/language-reference/operators/boolean-logical-operators.md index efbe493a4639c..7360d387a25af 100644 --- a/docs/csharp/language-reference/operators/boolean-logical-operators.md +++ b/docs/csharp/language-reference/operators/boolean-logical-operators.md @@ -182,7 +182,7 @@ For the complete list of C# operators ordered by precedence level, see the [Oper ## Operator overloadability -A user-defined type can [overload](operator-overloading.md) the `!`, `&`, `|`, and `^` operators. When a binary operator is overloaded, the corresponding compound assignment operator is also implicitly overloaded. Beginning with C# 14, a user-defined type can explicitly overload the compound assignment operators to provide a more efficient implementation. Typically, a type overloads these operators because the value can be updated in place, rather than allocating performing the addition and return a new instance. If a type doesn't provide an explicit overload, the compiler generates the implicit overload. +A user-defined type can [overload](operator-overloading.md) the `!`, `&`, `|`, and `^` operators. When a binary operator is overloaded, the corresponding compound assignment operator is also implicitly overloaded. Beginning with C# 14, a user-defined type can explicitly overload the compound assignment operators to provide a more efficient implementation. Typically, a type overloads these operators because the value can be updated in place, rather than allocating a new instance to hold the result of the binary operation. If a type doesn't provide an explicit overload, the compiler generates the implicit overload. A user-defined type can't overload the conditional logical operators `&&` and `||`. However, if a user-defined type overloads the [true and false operators](true-false-operators.md) and the `&` or `|` operator in a certain way, the `&&` or `||` operation, respectively, can be evaluated for the operands of that type. For more information, see the [User-defined conditional logical operators](~/_csharpstandard/standard/expressions.md#12143-user-defined-conditional-logical-operators) section of the [C# language specification](~/_csharpstandard/standard/README.md). diff --git a/docs/csharp/language-reference/operators/subtraction-operator.md b/docs/csharp/language-reference/operators/subtraction-operator.md index 2492adb1f619b..05d1743fe688d 100644 --- a/docs/csharp/language-reference/operators/subtraction-operator.md +++ b/docs/csharp/language-reference/operators/subtraction-operator.md @@ -65,7 +65,7 @@ You also use the `-=` operator to specify an event handler method to remove when ## Operator overloadability -A user-defined type can [overload](operator-overloading.md) the `-` operator. When a binary `-` operator is overloaded, the `-=` operator is also implicitly overloaded. Beginning with C# 14, a user-defined type can explicitly overload the `-=` operator to provide a more efficient implementation. Typically, a type overloads the `-=` operator because the value can be updated in place, rather than allocating performing the addition and return a new instance. If a type doesn't provide an explicit overload, the compiler generates the implicit overload. +A user-defined type can [overload](operator-overloading.md) the `-` operator. When a binary `-` operator is overloaded, the `-=` operator is also implicitly overloaded. Beginning with C# 14, a user-defined type can explicitly overload the `-=` operator to provide a more efficient implementation. Typically, a type overloads the `-=` operator because the value can be updated in place, rather than allocating a new instance to store the result of the subtraction. If a type doesn't provide an explicit overload, the compiler generates the implicit overload. ## C# language specification