Implement IEquatable for struct, and pass by reference

194 Views Asked by At

Is it possible to implement IEquatable from a struct, but pass the compared argument by reference?

Example:

struct BigToCopy : IEquatable<BigToCopy>
{
    int x, y, z, w, foo bar, etc;

    bool Equals(in BigToCopy other) // does not compile with 'in' parameter
    {
    }
}

It does not compile, because seemingly it does not implement IEquatable<BigToCopy> when there is the 'in' keyword.

This one is ok, but it copies by value:

struct BigToCopy : IEquatable<BigToCopy>
{
    int x, y, z, w, foo bar, etc;

    bool Equals(BigToCopy other) // does compile
    {
    }
}

IEqualityComparer has the same problem as far as I can tell. It requires the arguments to be passed by value.

Would compiler optimizations avoid copying even if we pass by value? Note: I'm on Unity. Using mono and il2cpp.

Edit: ok, let's be more concrete. We have a number of classes in our code base that are implemented like this:

struct Vertex
{
    public Point3d Position; // 3 doubles
    public Vector3d Normal; // 3 doubles
    public Vector2d UV; // 2 doubles
    public Color Color; // 4 bytes
    // possibly some other vertex data

    public override bool Equals(object obj) { ... }
    public bool Equals(in Vertex vertex) { ... }
}

There are multiple such classes. They are put in collections as HashSets, Dictionaries, etc. They are also being compared by explicit calls of Equals in some cases. These objects can be created in thousands, maybe even millions, and are processed in some way. They are usually in a hot code path.

Recently, I have found out that dictionary lookup using these objects can lead to object allocation. The reason: these objects don't implement IEquatable. Therefore, the dictionary calls Equals(object obj) overload instead, which leads to boxing (= memory allocation).

I'm fixing these classes right now, by implementing the IEquatable interface and removing the 'in' keyword. But there is some existing code that calls these Equals methods directly, and I was not sure whether I was affecting the performance badly. I could try, but there are many classes I'm fixing this way, so it is too much work to check. Instead, I add another method:

public bool EqualsPassByRef(in Vertex vertex) { ... }

and replace explicit calls of Equals by EqualsPassByRef.

It works ok this way. The performance would be better compared to before. I just wondered: maybe there is a way to make C# call the 'in' version from the dictionary. Then the 'EqualsPassByRef' versions would not be needed, making the code look better (and possibly also faster in dictionary lookup). From the answers I conclude that it is not possible. Ok, that's still fine.

Btw: I'm new to C#. I'm coming from a C++ world.

0

There are 0 best solutions below