User defined compound assignment operators #9100
Replies: 6 comments 11 replies
-
For functional and immutability perspective, how to make sure the instance isn't shared inappropriately? Like: BigInteger a = 1;
BigInteger b = a;
a += 1; Should this be equivalent to the following? BigInteger a = 1;
BigInteger b = a;
a = a + 1; It may also cause concern about thread safety etc. In other words, reusing storages in compound assignment are only safe if unique ownership is provided. Although this doesn't affect the operator support in C# itself, it can affect API designs leveraging this pattern. Can we provide more? |
Beta Was this translation helpful? Give feedback.
-
I was excited to see this feature proposal :)
Re this: allowing Also, another question is how it works in |
Beta Was this translation helpful? Give feedback.
-
As noted in another answer, this proposal does not add much (if anything) for the existing kinds of types. However, for move-only types it does add new capabilities. If you have a move-only type behind a The workaround with the existing language is to use a method which can take A subtle side effect of the proposal will be that the order of evaluation will be different for types that adopt user-defined compound assignment operators. Normally, A contrived example: int x = 1;
x += f();
Console.WriteLine($"x = {x}");
int f() {
x = 100;
return 4;
} This prints |
Beta Was this translation helpful? Give feedback.
-
The spec proposes that when there is an overload resolution failure among declared compound operators (e.g. This makes it a breaking change to add any compound assignment operator to a type in the following scenario: var x = new MyType();
x += 4; // Breaks if the `+=(MyType, MyType)` operator below is added, and the `+=(MyType, int)` operator is overlooked.
class MyType
{
public void operator +=(MyType right) => ...
public static MyType operator +(MyType left, MyType right) => ...
public static MyType operator +(MyType left, int right) => ...
// Irrelevant to the example, but often declared:
public static MyType operator +(int left, MyType right) => ...
} For safety, if any particular compound operator is declared, I propose a compiler warning if it is missing any overloads that are present among its matching non-compound operator declarations. (Non-compound operator overloads where the first operand is a different type can't have a compound operator counterpart, and are ignored for this check.) |
Beta Was this translation helpful? Give feedback.
This comment has been hidden.
This comment has been hidden.
-
I believe adding compound assignment operators to C# is an awesome idea. However... If I read this correctly, then this proposal explicitly violates current CLS requirements that have been in place for over two decades. Why? F# has had compound assignment operators for over a decade. F# relies on CLS compliance: it simply replaces With this proposal, compound assignment operators defined in C# will not work in F#. Depending on implementation details (e.g. if it relies on the return type being void), it may break code that already defines CLS-compliant compound assignment methods that F# does pick up. Worse, there may not be an easy way to fix it, since this would mean defining a method with the same parameters but a different return type. So why not do the obvious: leave increment/decrement alone and treat |
Beta Was this translation helpful? Give feedback.
-
Champion issue: #9101
Allow user types to customize behavior of compound assignment operators in a way that the target of the assignment is
modified in-place.
Motivation
C# provides support for the developer overloading operator implementations for user-defined type.
Additionally, it provides support for "compound assignment operators" which allow the user to write
code similarly to
x += y
rather thanx = x + y
. However, the language does not currently allowfor the developer to overload these compound assignment operators and while the default behavior
does the right thing, especially as it pertains to immutable value types, it is not always
"optimal".
Given the following example
with the current language rules, compound assignment operator
c1 += 1
invokes user defined+
operatorand then assigns its return value to the local variable
c1
. Note that operator implementation must allocateand return a new instance of
C1
, while, from the consumer's perspective, an in-place change to the originalinstance of
C1
instead would work as good (it is not used after the assignment), with an additional benefit ofavoiding an extra allocation.
When a program utilizes a compound assignment operation, the most common effect is that the original value is
"lost" and is no longer available to the program. With types which have large data (such as BigInteger, Tensors, etc.)
the cost of producing a net new destination, iterating, and copying the memory tends to be fairly expensive.
An in-place mutation would allow skipping this expense in many cases, which can provide significant improvements
to such scenarios.
Therefore, it may be beneficial for C# to allow user types to
customize behavior of compound assignment operators and optimize scenarios that would otherwise need to allocate
and copy.
Beta Was this translation helpful? Give feedback.
All reactions