dotnet / csharplang

The official repo for the design of the C# programming language
11.48k stars 1.02k forks source link

Allow ref as a parameter modifier to mark on the left one of an operator overloading #3871

Closed KyouyamaKazusa0805 closed 4 years ago

KyouyamaKazusa0805 commented 4 years ago

Consider the code:

public static GridMap operator +(GridMap left, GridMap right)
{
    var result = left;
    foreach (int offset in right)
    {
        result.Add(offset);
    }

    return result;
}

In the code, GridMap is an example for overload the operator +. If the type GridMap is a value type, the result will be passed by value and got a copy.

If we allow the code:

public static GridMap operator +(ref GridMap left, GridMap right)
{
    var result = left;
    foreach (int offset in right)
    {
        result.Add(offset);
    }

    return result;
}

We can use the compound operator operator += (e.g. a += b will be translated to op_Addition(ref a, b)) to reduce the unnecessary allocation and to improve the performance.

I know why we cannot overload the operator += and other compound assignment operators directly in C#. However, if we change a way to implement this, the feedback will be to enhance the performance, at the same time to avoid the wrong using on the custom overloading operator op= directly.

Thanks.

MrJul commented 4 years ago

You can use in (readonly ref) for that since C# 7.2: public static GridMap operator +(in GridMap left, in GridMap right) { ... }

The only copy should then be for the resulting struct.

KyouyamaKazusa0805 commented 4 years ago

Oh! What a great design! I don't know in can be used on an operator overloads! Thank you very much!!! 😆

I know that ref or out cannot be a modifier, but this keyword, I almost totally forgot it!

I'll close this! Thank you!

KyouyamaKazusa0805 commented 4 years ago

@MrJul But I wonder, if we overloads with-in and without-in two operator overloads at the same time, how to tell them? For example, if we overloads two same operators, but one is used for add a value without any side effects, and another is used for compound add assignment.

public static S operator +(S left, S right) { ... }
public static S operator +(in S left, in S right) { ... }
MrJul commented 4 years ago

in makes the reference read-only so you can't mutate the original struct anyway: you're only avoiding the extra copies. a += b is still compiled as a = a + b, in doesn't change that.

As per #945 the overload without in will always be preferred. Since it's an operator, you can't manually add in at call site to choose a different overload. So don't make two overloads, you won't gain anything.

And remember that operators should never have side effects! :)

KyouyamaKazusa0805 commented 4 years ago

Now I get it! Thank you for your patience :P